diff options
Diffstat (limited to 'indra/newview')
93 files changed, 5395 insertions, 4561 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index a61c35abd2..af6beacdfa 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -530,6 +530,7 @@ set(viewer_SOURCE_FILES      llviewerregion.cpp      llviewershadermgr.cpp      llviewerstats.cpp +    llviewerstatsrecorder.cpp      llviewertexteditor.cpp      llviewertexture.cpp      llviewertextureanim.cpp @@ -1063,6 +1064,7 @@ set(viewer_HEADER_FILES      llviewerregion.h      llviewershadermgr.h      llviewerstats.h +    llviewerstatsrecorder.h      llviewertexteditor.h      llviewertexture.h      llviewertextureanim.h @@ -1716,6 +1718,17 @@ set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH  if (LINUX)    set(product SecondLife-${ARCH}-${viewer_VERSION}) +  # These are the generated targets that are copied to package/ +  set(COPY_INPUT_DEPENDENCIES +	${VIEWER_BINARY_NAME} +	linux-crash-logger +	linux-updater +	SLPlugin +	media_plugin_webkit +	media_plugin_gstreamer010 +	llcommon +	) +    add_custom_command(        OUTPUT ${product}.tar.bz2        COMMAND ${PYTHON_EXECUTABLE} @@ -1733,18 +1746,11 @@ if (LINUX)          --login_channel=${VIEWER_LOGIN_CHANNEL}          --source=${CMAKE_CURRENT_SOURCE_DIR}          --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.touched -      DEPENDS ${VIEWER_BINARY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py +      DEPENDS +        ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py +        ${COPY_INPUT_DEPENDENCIES}        ) -  add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_gstreamer010 media_plugin_webkit) - -  if (PACKAGE) -    add_custom_target(package ALL DEPENDS ${product}.tar.bz2) -    add_dependencies(package linux-crash-logger-target) -    add_dependencies(package linux-updater-target) -    check_message_template(package) -  endif (PACKAGE) -    add_custom_command(      OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched      COMMAND ${PYTHON_EXECUTABLE} @@ -1764,9 +1770,15 @@ if (LINUX)        ${COPY_INPUT_DEPENDENCIES}      COMMENT "Performing viewer_manifest copy"      ) -     +    add_custom_target(copy_l_viewer_manifest ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/.${product}.copy_touched)  -  add_dependencies(copy_l_viewer_manifest "${VIEWER_BINARY_NAME}" linux-crash-logger-target linux-updater-target) + +  if (PACKAGE) +    add_custom_target(package ALL DEPENDS ${product}.tar.bz2) +    # Make sure we don't run two instances of viewer_manifest.py at the same time. +    add_dependencies(package copy_l_viewer_manifest) +    check_message_template(package) +  endif (PACKAGE)  endif (LINUX)  if (DARWIN) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ca587302b2..f72fea1cfb 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -928,39 +928,6 @@        <key>Value</key>        <integer>1</integer>      </map> -    <key>BulkChangeIncludeAnimations</key> -    <map> -      <key>Comment</key> -      <string>Bulk permission changes affect animations</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>1</integer> -    </map> -    <key>BulkChangeIncludeAnimations</key> -    <map> -      <key>Comment</key> -      <string>Bulk permission changes affect animations</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>1</integer> -    </map> -    <key>BulkChangeIncludeAnimations</key> -    <map> -      <key>Comment</key> -      <string>Bulk permission changes affect animations</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>1</integer> -    </map>      <key>BulkChangeIncludeBodyParts</key>      <map>        <key>Comment</key> @@ -1173,18 +1140,7 @@      <key>CacheLocationTopFolder</key>      <map>        <key>Comment</key> -      <string>Controls the top folder location of the local disk cache</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>String</string> -      <key>Value</key> -      <string /> -    </map>	 -    <key>CacheLocationTopFolder</key> -    <map> -      <key>Comment</key> -      <string>Controls the location of the local disk cache</string> +      <string>Controls the top folder location of the the local disk cache</string>        <key>Persist</key>        <integer>1</integer>        <key>Type</key> @@ -1201,7 +1157,7 @@        <key>Type</key>        <string>U32</string>        <key>Value</key> -      <integer>20000</integer> +      <integer>128</integer>      </map>      <key>CacheSize</key>      <map> @@ -1896,10 +1852,21 @@      <key>Value</key>      <integer>0</integer>    </map> +  <key>DebugShowTextureInfo</key> +    <map> +      <key>Comment</key> +      <string>Show inertested texture info</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map>    <key>DebugShowTime</key>      <map>        <key>Comment</key> -      <string>Show depth buffer contents</string> +      <string>Show time info</string>        <key>Persist</key>        <integer>1</integer>        <key>Type</key> @@ -3097,17 +3064,6 @@          <string>http://viewer-settings.secondlife.com</string>      </map>      <key>FPSLogFrequency</key> -    <map> -      <key>Comment</key> -      <string>Seconds between display of FPS in log (0 for never)</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>F32</string> -      <key>Value</key> -      <real>60.0</real> -    </map> -    <key>FPSLogFrequency</key>          <map>          <key>Comment</key>              <string>Seconds between display of FPS in log (0 for never)</string> @@ -6062,17 +6018,6 @@      <key>OutBandwidth</key>      <map>        <key>Comment</key> -      <string>Expand render stats display</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>1</integer> -    </map> -    <key>OutBandwidth</key> -    <map> -      <key>Comment</key>        <string>Outgoing bandwidth throttle (bps)</string>        <key>Persist</key>        <integer>1</integer> @@ -11430,8 +11375,6 @@        <key>Type</key>        <string>LLSD</string>        <key>Value</key> -      <map> -      </map>      </map>      <key>VFSOldSize</key>      <map> @@ -11587,17 +11530,6 @@        <key>Value</key>        <string></string>      </map> -    <key>VivoxDebugSIPURIHostName</key> -    <map> -      <key>Comment</key> -      <string>Hostname portion of vivox SIP URIs (empty string for the default).</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>String</string> -      <key>Value</key> -      <string></string> -    </map>      <key>VivoxDebugVoiceAccountServerURI</key>      <map>        <key>Comment</key> @@ -12423,16 +12355,5 @@        <key>Value</key>        <string>name</string>      </map> -    <key>ReleaseNotesURL</key> -    <map> -      <key>Comment</key> -      <string>Release notes URL template</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>String</string> -      <key>Value</key> -      <string>http://secondlife.com/app/releasenotes/?channel=[CHANNEL]&version=[VERSION]</string> -    </map>  </map>  </llsd> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 77552663ab..7d908df5ce 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -64,6 +64,7 @@  #include "lltool.h"  #include "lltoolmgr.h"  #include "lltrans.h" +#include "llurlentry.h"  #include "llviewercontrol.h"  #include "llviewerdisplay.h"  #include "llviewerjoystick.h" @@ -649,6 +650,10 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  	}  	mRegionp = regionp; +	// Pass the region host to LLUrlEntryParcel to resolve parcel name +	// with a server request. +	LLUrlEntryParcel::setRegionHost(getRegionHost()); +  	// Must shift hole-covering water object locations because local  	// coordinate frame changed.  	LLWorld::getInstance()->updateWaterObjects(); diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 80734b0d41..f40fed5ad3 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1300,8 +1300,16 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)  		return false;  	} -	// Check whether the outfit contains the full set of body parts (shape+skin+hair+eyes). -	return getCanMakeFolderIntoOutfit(outfit_cat_id); +	// Check whether the outfit contains any wearables we aren't wearing already (STORM-702). +	LLInventoryModel::cat_array_t cats; +	LLInventoryModel::item_array_t items; +	LLFindWearablesEx is_worn(/*is_worn=*/ false, /*include_body_parts=*/ true); +	gInventory.collectDescendentsIf(outfit_cat_id, +		cats, +		items, +		LLInventoryModel::EXCLUDE_TRASH, +		is_worn); +	return items.size() > 0;  }  void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e92042bcd4..6a9dfaf21b 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -44,6 +44,7 @@  #include "llagentwearables.h"  #include "llwindow.h"  #include "llviewerstats.h" +#include "llviewerstatsrecorder.h"  #include "llmd5.h"  #include "llpumpio.h"  #include "llmimetypes.h" @@ -93,6 +94,7 @@  #include "llmemory.h"  #include "llprimitive.h"  #include "llurlaction.h" +#include "llurlentry.h"  #include "llvfile.h"  #include "llvfsthread.h"  #include "llvolumemgr.h" @@ -471,8 +473,6 @@ static void settings_to_globals()  	gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc");  	gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates");  	LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale"); - -	LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap");  }  static void settings_modify() @@ -666,6 +666,10 @@ bool LLAppViewer::init()      mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); +#if LL_RECORD_VIEWER_STATS +	LLViewerStatsRecorder::initClass(); +#endif +      // *NOTE:Mani - LLCurl::initClass is not thread safe.       // Called before threads are created.      LLCurl::initClass(); @@ -848,6 +852,9 @@ bool LLAppViewer::init()  	gGLActive = TRUE;  	initWindow(); +	// initWindow also initializes the Feature List, so now we can initialize this global. +	LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap"); +  	// call all self-registered classes  	LLInitClassList::instance().fireCallbacks(); @@ -989,6 +996,8 @@ bool LLAppViewer::init()  	LLAgentLanguage::init(); + +  	return true;  } @@ -1389,16 +1398,6 @@ bool LLAppViewer::cleanup()  	}  	mPlugins.clear(); -	//---------------------------------------------- -	//this test code will be removed after the test -	//test manual call stack tracer -	if(gSavedSettings.getBOOL("QAMode")) -	{ -		LLError::LLCallStacks::print() ; -	} -	//end of the test code -	//---------------------------------------------- -  	//flag all elements as needing to be destroyed immediately  	// to ensure shutdown order  	LLMortician::setZealous(TRUE); @@ -1709,6 +1708,10 @@ bool LLAppViewer::cleanup()  	}  	LLMetricPerformanceTesterBasic::cleanClass() ; +#if LL_RECORD_VIEWER_STATS +	LLViewerStatsRecorder::cleanupClass(); +#endif +  	llinfos << "Cleaning up Media and Textures" << llendflush;  	//Note: @@ -3064,35 +3067,32 @@ void LLAppViewer::initMarkerFile()  	std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME);  	std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); -	  	if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB) && !anotherInstanceRunning())  	{  		gLastExecEvent = LAST_EXEC_FROZE;  		LL_INFOS("MarkerFile") << "Exec marker found: program froze on previous execution" << LL_ENDL;  	}     -      	if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB))  	{ -		LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << LAST_EXEC_LLERROR_CRASH << LL_ENDL;  		gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; +		LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; +		LLAPRFile::remove(logout_marker_file);  	}  	if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB))  	{ -		llinfos << "Last exec LLError crashed, setting LastExecEvent to " << LAST_EXEC_LLERROR_CRASH << llendl;  		if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;  		else gLastExecEvent = LAST_EXEC_LLERROR_CRASH; +		LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; +		LLAPRFile::remove(llerror_marker_file);  	}  	if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))  	{ -		LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << LAST_EXEC_OTHER_CRASH << LL_ENDL;  		if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH;  		else gLastExecEvent = LAST_EXEC_OTHER_CRASH; +		LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; +		LLAPRFile::remove(error_marker_file);  	} -	 -	LLAPRFile::remove(logout_marker_file); -	LLAPRFile::remove(llerror_marker_file); -	LLAPRFile::remove(error_marker_file); -	 +  	// No new markers if another instance is running.  	if(anotherInstanceRunning())   	{ @@ -4568,6 +4568,10 @@ void LLAppViewer::disconnectViewer()  	cleanup_xfer_manager();  	gDisconnected = TRUE; + +	// Pass the connection state to LLUrlEntryParcel not to attempt +	// parcel info requests while disconnected. +	LLUrlEntryParcel::setDisconnected(gDisconnected);  }  void LLAppViewer::forceErrorLLError() diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index f12bc16d4b..dd5bc74b2a 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -126,6 +126,7 @@ void LLAssetUploadResponder::error(U32 statusNum, const std::string& reason)  			break;  	}  	LLUploadDialog::modalUploadFinished(); +	LLFilePicker::instance().reset();  // unlock file picker when bulk upload fails  }  //virtual  diff --git a/indra/newview/llcapabilityprovider.h b/indra/newview/llcapabilityprovider.h index a6e743f625..9d91245597 100644 --- a/indra/newview/llcapabilityprovider.h +++ b/indra/newview/llcapabilityprovider.h @@ -46,7 +46,7 @@ public:      /**       * Get host to which to send that capability request.       */ -    virtual LLHost getHost() const = 0; +    virtual const LLHost& getHost() const = 0;      /**       * Describe this LLCapabilityProvider for logging etc.       */ diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index 4a1ba6f1b5..6f02192d0a 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -319,7 +319,7 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )  // This is called when the main floatercustomize panel is closed.  // Since this class has pointers up to its parents, we need to cleanup  // this class first in order to avoid a crash. -void LLColorSwatchCtrl::onParentFloaterClosed() +void LLColorSwatchCtrl::closeFloaterColorPicker()  {  	LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)mPickerHandle.get();  	if (pickerp) diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h index cd859ea128..5bdd1712d2 100644 --- a/indra/newview/llcolorswatch.h +++ b/indra/newview/llcolorswatch.h @@ -100,7 +100,7 @@ public:  	/*virtual*/ void	setEnabled( BOOL enabled );  	static void		onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE ); -	void			onParentFloaterClosed(); +	void			closeFloaterColorPicker();  protected:  	BOOL			mValid; diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index a3d2941114..f781d5f3ff 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -177,10 +177,6 @@ void LLViewerDynamicTexture::postRender(BOOL success)  				generateGLTexture() ;  			} -			if(gGLManager.mDebugGPU) -			{ -				LLGLState::dumpStates() ; -			}  			success = mGLTexturep->setSubImageFromFrameBuffer(0, 0, mOrigin.mX, mOrigin.mY, mFullWidth, mFullHeight);  		}  	} @@ -220,12 +216,6 @@ BOOL LLViewerDynamicTexture::updateAllInstances()  			LLViewerDynamicTexture *dynamicTexture = *iter;  			if (dynamicTexture->needsRender())  			{				 -				if(gGLManager.mDebugGPU) -				{				 -					llinfos << "class type: " << (S32)dynamicTexture->getType() << llendl; -					LLGLState::dumpStates() ; -				} -  				glClear(GL_DEPTH_BUFFER_BIT);  				gDepthDirty = TRUE; diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 2471da9da5..fe201a6773 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1155,7 +1155,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,  		if (rebuild_tcoord)  		{  			LLVector2 tc = vf.mVertices[i].mTexCoord; -		 +		     			if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)  			{  				LLVector3 vec = vf.mVertices[i].mPosition;  @@ -1331,7 +1331,14 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume,  		mTexExtents[0].setVec(0,0);  		mTexExtents[1].setVec(1,1);  		xform(mTexExtents[0], cos_ang, sin_ang, os, ot, ms, mt); -		xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt);		 +		xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt); +		 +		F32 es = vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0] ; +		F32 et = vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1] ; +		mTexExtents[0][0] *= es ; +		mTexExtents[1][0] *= es ; +		mTexExtents[0][1] *= et ; +		mTexExtents[1][1] *= et ;  	}  	mLastVertexBuffer = mVertexBuffer; diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index ca2ef5f5b8..4e16cc4217 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -290,11 +290,9 @@ BOOL LLFeatureManager::parseFeatureTable(std::string filename)  	mTableVersion = version;  	LLFeatureList *flp = NULL; -	while (!file.eof() && file.good()) +	while (file >> name)  	{  		char buffer[MAX_STRING];		 /*Flawfinder: ignore*/ - -		file >> name;  		if (name.substr(0,2) == "//")  		{ @@ -303,13 +301,6 @@ BOOL LLFeatureManager::parseFeatureTable(std::string filename)  			continue;  		} -		if (name.empty()) -		{ -			// This is a blank line -			file.getline(buffer, MAX_STRING); -			continue; -		} -  		if (name == "list")  		{  			if (flp) diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 71882fbb83..07f5220ab7 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -178,7 +178,7 @@ void LLFloaterSettingsDebug::onClickDefault()  	if (controlp)  	{ -		controlp->resetToDefault(); +		controlp->resetToDefault(true);  		updateControl(controlp);  	}  } diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 51726112a0..058567492b 100644 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -1,402 +1,402 @@ -/**
 - * @file llfloaterwebcontent.cpp
 - * @brief floater for displaying web content - e.g. profiles and search (eventually)
 - *
 - * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 - * Second Life Viewer Source Code
 - * Copyright (C) 2010, Linden Research, Inc.
 - *
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Lesser General Public
 - * License as published by the Free Software Foundation;
 - * version 2.1 of the License only.
 - *
 - * This library is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Lesser General Public License for more details.
 - *
 - * You should have received a copy of the GNU Lesser General Public
 - * License along with this library; if not, write to the Free Software
 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 - *
 - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 - * $/LicenseInfo$
 - */
 -
 -#include "llviewerprecompiledheaders.h"
 -
 -#include "llcombobox.h"
 -#include "lliconctrl.h"
 -#include "llfloaterreg.h"
 -#include "lllayoutstack.h"
 -#include "llpluginclassmedia.h"
 -#include "llprogressbar.h"
 -#include "lltextbox.h"
 -#include "llurlhistory.h"
 -#include "llviewercontrol.h"
 -#include "llweb.h"
 -#include "llwindow.h"
 -
 -#include "llfloaterwebcontent.h"
 -
 -LLFloaterWebContent::LLFloaterWebContent( const LLSD& key )
 -	: LLFloater( key )
 -{
 -	mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this ));
 -	mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this ));
 -	mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this ));
 -	mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this ));
 -	mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this ));
 -	mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this ));
 -}
 -
 -BOOL LLFloaterWebContent::postBuild()
 -{
 -	// these are used in a bunch of places so cache them
 -	mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" );
 -	mAddressCombo = getChild< LLComboBox >( "address" );
 -	mStatusBarText = getChild< LLTextBox >( "statusbartext" );
 -	mStatusBarProgress = getChild<LLProgressBar>("statusbarprogress" );
 -
 -	// observe browser events
 -	mWebBrowser->addObserver( this );
 -
 -	// these buttons are always enabled
 -	getChildView("reload")->setEnabled( true );
 -	getChildView("popexternal")->setEnabled( true );
 -
 -	// cache image for secure browsing
 -	mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag");
 -
 -	// initialize the URL history using the system URL History manager
 -	initializeURLHistory();
 -
 -	return TRUE;
 -}
 -
 -void LLFloaterWebContent::initializeURLHistory()
 -{
 -	// start with an empty list
 -	LLCtrlListInterface* url_list = childGetListInterface("address");
 -	if (url_list)
 -	{
 -		url_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
 -	}
 -
 -	// Get all of the entries in the "browser" collection
 -	LLSD browser_history = LLURLHistory::getURLHistory("browser");
 -	LLSD::array_iterator iter_history =
 -		browser_history.beginArray();
 -	LLSD::array_iterator end_history =
 -		browser_history.endArray();
 -	for(; iter_history != end_history; ++iter_history)
 -	{
 -		std::string url = (*iter_history).asString();
 -		if(! url.empty())
 -			url_list->addSimpleElement(url);
 -	}
 -}
 -
 -//static
 -void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid )
 -{
 -	lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl;
 -
 -	std::string tag = target;
 -
 -	if(target.empty() || target == "_blank")
 -	{
 -		if(!uuid.empty())
 -		{
 -			tag = uuid;
 -		}
 -		else
 -		{
 -			// create a unique tag for this instance
 -			LLUUID id;
 -			id.generate();
 -			tag = id.asString();
 -		}
 -	}
 -
 -	S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit");
 -
 -	if(LLFloaterReg::findInstance("web_content", tag) != NULL)
 -	{
 -		// There's already a web browser for this tag, so we won't be opening a new window.
 -	}
 -	else if(browser_window_limit != 0)
 -	{
 -		// showInstance will open a new window.  Figure out how many web browsers are already open,
 -		// and close the least recently opened one if this will put us over the limit.
 -
 -		LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content");
 -		lldebugs << "total instance count is " << instances.size() << llendl;
 -
 -		for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++)
 -		{
 -			lldebugs << "    " << (*iter)->getKey() << llendl;
 -		}
 -
 -		if(instances.size() >= (size_t)browser_window_limit)
 -		{
 -			// Destroy the least recently opened instance
 -			(*instances.begin())->closeFloater();
 -		}
 -	}
 -
 -	LLFloaterWebContent *browser = dynamic_cast<LLFloaterWebContent*> (LLFloaterReg::showInstance("web_content", tag));
 -	llassert(browser);
 -	if(browser)
 -	{
 -		browser->mUUID = uuid;
 -
 -		// tell the browser instance to load the specified URL
 -		browser->open_media(url, target);
 -		LLViewerMedia::proxyWindowOpened(target, uuid);
 -	}
 -}
 -
 -//static
 -void LLFloaterWebContent::closeRequest(const std::string &uuid)
 -{
 -	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
 -	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
 -	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
 -	{
 -		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
 -		lldebugs << "    " << i->mUUID << llendl;
 -		if (i && i->mUUID == uuid)
 -		{
 -			i->closeFloater(false);
 -			return;
 - 		}
 - 	}
 -}
 -
 -//static
 -void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height)
 -{
 -	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content");
 -	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl;
 -	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter)
 -	{
 -		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter);
 -		lldebugs << "    " << i->mUUID << llendl;
 -		if (i && i->mUUID == uuid)
 -		{
 -			i->geometryChanged(x, y, width, height);
 -			return;
 -		}
 -	}
 -}
 -
 -void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height)
 -{
 -	// Make sure the layout of the browser control is updated, so this calculation is correct.
 -	LLLayoutStack::updateClass();
 -
 -	// TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc.
 -	LLCoordWindow window_size;
 -	getWindow()->getSize(&window_size);
 -
 -	// Adjust width and height for the size of the chrome on the web Browser window.
 -	width += getRect().getWidth() - mWebBrowser->getRect().getWidth();
 -	height += getRect().getHeight() - mWebBrowser->getRect().getHeight();
 -
 -	LLRect geom;
 -	geom.setOriginAndSize(x, window_size.mY - (y + height), width, height);
 -
 -	lldebugs << "geometry change: " << geom << llendl;
 -
 -	handleReshape(geom,false);
 -}
 -
 -void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target)
 -{
 -	// Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin.
 -	mWebBrowser->setHomePageUrl(web_url, "text/html");
 -	mWebBrowser->setTarget(target);
 -	mWebBrowser->navigateTo(web_url, "text/html");
 -	set_current_url(web_url);
 -}
 -
 -//virtual
 -void LLFloaterWebContent::onClose(bool app_quitting)
 -{
 -	LLViewerMedia::proxyWindowClosed(mUUID);
 -	destroy();
 -}
 -
 -// virtual
 -void LLFloaterWebContent::draw()
 -{
 -	// this is asychronous so we need to keep checking
 -	getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() );
 -	getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() );
 -
 -	LLFloater::draw();
 -}
 -
 -// virtual
 -void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
 -{
 -	if(event == MEDIA_EVENT_LOCATION_CHANGED)
 -	{
 -		const std::string url = self->getLocation();
 -
 -		if ( url.length() )
 -			mStatusBarText->setText( url );
 -
 -		set_current_url( url );
 -	}
 -	else if(event == MEDIA_EVENT_NAVIGATE_BEGIN)
 -	{
 -		// flags are sent with this event
 -		getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
 -		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
 -
 -		// toggle visibility of these buttons based on browser state
 -		getChildView("reload")->setVisible( false );
 -		getChildView("stop")->setVisible( true );
 -
 -		// turn "on" progress bar now we're about to start loading
 -		mStatusBarProgress->setVisible( true );
 -	}
 -	else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE)
 -	{
 -		// flags are sent with this event
 -		getChildView("back")->setEnabled( self->getHistoryBackAvailable() );
 -		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() );
 -
 -		// toggle visibility of these buttons based on browser state
 -		getChildView("reload")->setVisible( true );
 -		getChildView("stop")->setVisible( false );
 -
 -		// turn "off" progress bar now we're loaded
 -		mStatusBarProgress->setVisible( false );
 -
 -		// we populate the status bar with URLs as they change so clear it now we're done
 -		const std::string end_str = "";
 -		mStatusBarText->setText( end_str );
 -
 -		// decide if secure browsing icon should be displayed
 -		std::string prefix =  std::string("https://");
 -		std::string test_prefix = mCurrentURL.substr(0, prefix.length());
 -		LLStringUtil::toLower(test_prefix);
 -		if(test_prefix == prefix)
 -		{
 -			mSecureLockIcon->setVisible(true);
 -		}
 -		else
 -		{
 -			mSecureLockIcon->setVisible(false);
 -		}
 -	}
 -	else if(event == MEDIA_EVENT_CLOSE_REQUEST)
 -	{
 -		// The browser instance wants its window closed.
 -		closeFloater();
 -	}
 -	else if(event == MEDIA_EVENT_GEOMETRY_CHANGE)
 -	{
 -		geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight());
 -	}
 -	else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED )
 -	{
 -		const std::string text = self->getStatusText();
 -		if ( text.length() )
 -			mStatusBarText->setText( text );
 -	}
 -	else if(event == MEDIA_EVENT_PROGRESS_UPDATED )
 -	{
 -		int percent = (int)self->getProgressPercent();
 -		mStatusBarProgress->setValue( percent );
 -	}
 -	else if(event == MEDIA_EVENT_NAME_CHANGED )
 -	{
 -		std::string page_title = self->getMediaName();
 -		// simulate browser behavior - title is empty, use the current URL
 -		if ( page_title.length() > 0 )
 -			setTitle( page_title );
 -		else
 -			setTitle( mCurrentURL );
 -	}
 -	else if(event == MEDIA_EVENT_LINK_HOVERED )
 -	{
 -		const std::string link = self->getHoverLink();
 -		mStatusBarText->setText( link );
 -	}
 -}
 -
 -void LLFloaterWebContent::set_current_url(const std::string& url)
 -{
 -	mCurrentURL = url;
 -
 -	// serialize url history into the system URL History manager
 -	LLURLHistory::removeURL("browser", mCurrentURL);
 -	LLURLHistory::addURL("browser", mCurrentURL);
 -
 -	mAddressCombo->remove( mCurrentURL );
 -	mAddressCombo->add( mCurrentURL );
 -	mAddressCombo->selectByValue( mCurrentURL );
 -}
 -
 -void LLFloaterWebContent::onClickForward()
 -{
 -	mWebBrowser->navigateForward();
 -}
 -
 -void LLFloaterWebContent::onClickBack()
 -{
 -	mWebBrowser->navigateBack();
 -}
 -
 -void LLFloaterWebContent::onClickReload()
 -{
 -
 -	if( mWebBrowser->getMediaPlugin() )
 -	{
 -		bool ignore_cache = true;
 -		mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache );
 -	}
 -	else
 -	{
 -		mWebBrowser->navigateTo(mCurrentURL);
 -	}
 -}
 -
 -void LLFloaterWebContent::onClickStop()
 -{
 -	if( mWebBrowser->getMediaPlugin() )
 -		mWebBrowser->getMediaPlugin()->browse_stop();
 -
 -	// still should happen when we catch the navigate complete event
 -	// but sometimes (don't know why) that event isn't sent from Qt
 -	// and we getto a point where the stop button stays active.
 -	getChildView("reload")->setVisible( true );
 -	getChildView("stop")->setVisible( false );
 -}
 -
 -void LLFloaterWebContent::onEnterAddress()
 -{
 -	// make sure there is at least something there.
 -	// (perhaps this test should be for minimum length of a URL)
 -	std::string url = mAddressCombo->getValue().asString();
 -	if ( url.length() > 0 )
 -	{
 -		mWebBrowser->navigateTo( url, "text/html");
 -	};
 -}
 -
 -void LLFloaterWebContent::onPopExternal()
 -{
 -	// make sure there is at least something there.
 -	// (perhaps this test should be for minimum length of a URL)
 -	std::string url = mAddressCombo->getValue().asString();
 -	if ( url.length() > 0 )
 -	{
 -		LLWeb::loadURLExternal( url );
 -	};
 -}
 +/** + * @file llfloaterwebcontent.cpp + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llcombobox.h" +#include "lliconctrl.h" +#include "llfloaterreg.h" +#include "lllayoutstack.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "lltextbox.h" +#include "llurlhistory.h" +#include "llviewercontrol.h" +#include "llweb.h" +#include "llwindow.h" + +#include "llfloaterwebcontent.h" + +LLFloaterWebContent::LLFloaterWebContent( const LLSD& key ) +	: LLFloater( key ) +{ +	mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); +	mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); +	mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); +	mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); +	mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); +	mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); +} + +BOOL LLFloaterWebContent::postBuild() +{ +	// these are used in a bunch of places so cache them +	mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" ); +	mAddressCombo = getChild< LLComboBox >( "address" ); +	mStatusBarText = getChild< LLTextBox >( "statusbartext" ); +	mStatusBarProgress = getChild<LLProgressBar>("statusbarprogress" ); + +	// observe browser events +	mWebBrowser->addObserver( this ); + +	// these buttons are always enabled +	getChildView("reload")->setEnabled( true ); +	getChildView("popexternal")->setEnabled( true ); + +	// cache image for secure browsing +	mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); + +	// initialize the URL history using the system URL History manager +	initializeURLHistory(); + +	return TRUE; +} + +void LLFloaterWebContent::initializeURLHistory() +{ +	// start with an empty list +	LLCtrlListInterface* url_list = childGetListInterface("address"); +	if (url_list) +	{ +		url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); +	} + +	// Get all of the entries in the "browser" collection +	LLSD browser_history = LLURLHistory::getURLHistory("browser"); +	LLSD::array_iterator iter_history = +		browser_history.beginArray(); +	LLSD::array_iterator end_history = +		browser_history.endArray(); +	for(; iter_history != end_history; ++iter_history) +	{ +		std::string url = (*iter_history).asString(); +		if(! url.empty()) +			url_list->addSimpleElement(url); +	} +} + +//static +void LLFloaterWebContent::create( const std::string &url, const std::string& target, const std::string& uuid ) +{ +	lldebugs << "url = " << url << ", target = " << target << ", uuid = " << uuid << llendl; + +	std::string tag = target; + +	if(target.empty() || target == "_blank") +	{ +		if(!uuid.empty()) +		{ +			tag = uuid; +		} +		else +		{ +			// create a unique tag for this instance +			LLUUID id; +			id.generate(); +			tag = id.asString(); +		} +	} + +	S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit"); + +	if(LLFloaterReg::findInstance("web_content", tag) != NULL) +	{ +		// There's already a web browser for this tag, so we won't be opening a new window. +	} +	else if(browser_window_limit != 0) +	{ +		// showInstance will open a new window.  Figure out how many web browsers are already open, +		// and close the least recently opened one if this will put us over the limit. + +		LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList("web_content"); +		lldebugs << "total instance count is " << instances.size() << llendl; + +		for(LLFloaterReg::const_instance_list_t::const_iterator iter = instances.begin(); iter != instances.end(); iter++) +		{ +			lldebugs << "    " << (*iter)->getKey() << llendl; +		} + +		if(instances.size() >= (size_t)browser_window_limit) +		{ +			// Destroy the least recently opened instance +			(*instances.begin())->closeFloater(); +		} +	} + +	LLFloaterWebContent *browser = dynamic_cast<LLFloaterWebContent*> (LLFloaterReg::showInstance("web_content", tag)); +	llassert(browser); +	if(browser) +	{ +		browser->mUUID = uuid; + +		// tell the browser instance to load the specified URL +		browser->open_media(url, target); +		LLViewerMedia::proxyWindowOpened(target, uuid); +	} +} + +//static +void LLFloaterWebContent::closeRequest(const std::string &uuid) +{ +	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); +	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; +	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) +	{ +		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter); +		lldebugs << "    " << i->mUUID << llendl; +		if (i && i->mUUID == uuid) +		{ +			i->closeFloater(false); +			return; + 		} + 	} +} + +//static +void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height) +{ +	LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("web_content"); +	lldebugs << "instance list size is " << inst_list.size() << ", incoming uuid is " << uuid << llendl; +	for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) +	{ +		LLFloaterWebContent* i = dynamic_cast<LLFloaterWebContent*>(*iter); +		lldebugs << "    " << i->mUUID << llendl; +		if (i && i->mUUID == uuid) +		{ +			i->geometryChanged(x, y, width, height); +			return; +		} +	} +} + +void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) +{ +	// Make sure the layout of the browser control is updated, so this calculation is correct. +	LLLayoutStack::updateClass(); + +	// TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. +	LLCoordWindow window_size; +	getWindow()->getSize(&window_size); + +	// Adjust width and height for the size of the chrome on the web Browser window. +	width += getRect().getWidth() - mWebBrowser->getRect().getWidth(); +	height += getRect().getHeight() - mWebBrowser->getRect().getHeight(); + +	LLRect geom; +	geom.setOriginAndSize(x, window_size.mY - (y + height), width, height); + +	lldebugs << "geometry change: " << geom << llendl; + +	handleReshape(geom,false); +} + +void LLFloaterWebContent::open_media(const std::string& web_url, const std::string& target) +{ +	// Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. +	mWebBrowser->setHomePageUrl(web_url, "text/html"); +	mWebBrowser->setTarget(target); +	mWebBrowser->navigateTo(web_url, "text/html"); +	set_current_url(web_url); +} + +//virtual +void LLFloaterWebContent::onClose(bool app_quitting) +{ +	LLViewerMedia::proxyWindowClosed(mUUID); +	destroy(); +} + +// virtual +void LLFloaterWebContent::draw() +{ +	// this is asychronous so we need to keep checking +	getChildView( "back" )->setEnabled( mWebBrowser->canNavigateBack() ); +	getChildView( "forward" )->setEnabled( mWebBrowser->canNavigateForward() ); + +	LLFloater::draw(); +} + +// virtual +void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ +	if(event == MEDIA_EVENT_LOCATION_CHANGED) +	{ +		const std::string url = self->getLocation(); + +		if ( url.length() ) +			mStatusBarText->setText( url ); + +		set_current_url( url ); +	} +	else if(event == MEDIA_EVENT_NAVIGATE_BEGIN) +	{ +		// flags are sent with this event +		getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); +		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); + +		// toggle visibility of these buttons based on browser state +		getChildView("reload")->setVisible( false ); +		getChildView("stop")->setVisible( true ); + +		// turn "on" progress bar now we're about to start loading +		mStatusBarProgress->setVisible( true ); +	} +	else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) +	{ +		// flags are sent with this event +		getChildView("back")->setEnabled( self->getHistoryBackAvailable() ); +		getChildView("forward")->setEnabled( self->getHistoryForwardAvailable() ); + +		// toggle visibility of these buttons based on browser state +		getChildView("reload")->setVisible( true ); +		getChildView("stop")->setVisible( false ); + +		// turn "off" progress bar now we're loaded +		mStatusBarProgress->setVisible( false ); + +		// we populate the status bar with URLs as they change so clear it now we're done +		const std::string end_str = ""; +		mStatusBarText->setText( end_str ); + +		// decide if secure browsing icon should be displayed +		std::string prefix =  std::string("https://"); +		std::string test_prefix = mCurrentURL.substr(0, prefix.length()); +		LLStringUtil::toLower(test_prefix); +		if(test_prefix == prefix) +		{ +			mSecureLockIcon->setVisible(true); +		} +		else +		{ +			mSecureLockIcon->setVisible(false); +		} +	} +	else if(event == MEDIA_EVENT_CLOSE_REQUEST) +	{ +		// The browser instance wants its window closed. +		closeFloater(); +	} +	else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) +	{ +		geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); +	} +	else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED ) +	{ +		const std::string text = self->getStatusText(); +		if ( text.length() ) +			mStatusBarText->setText( text ); +	} +	else if(event == MEDIA_EVENT_PROGRESS_UPDATED ) +	{ +		int percent = (int)self->getProgressPercent(); +		mStatusBarProgress->setValue( percent ); +	} +	else if(event == MEDIA_EVENT_NAME_CHANGED ) +	{ +		std::string page_title = self->getMediaName(); +		// simulate browser behavior - title is empty, use the current URL +		if ( page_title.length() > 0 ) +			setTitle( page_title ); +		else +			setTitle( mCurrentURL ); +	} +	else if(event == MEDIA_EVENT_LINK_HOVERED ) +	{ +		const std::string link = self->getHoverLink(); +		mStatusBarText->setText( link ); +	} +} + +void LLFloaterWebContent::set_current_url(const std::string& url) +{ +	mCurrentURL = url; + +	// serialize url history into the system URL History manager +	LLURLHistory::removeURL("browser", mCurrentURL); +	LLURLHistory::addURL("browser", mCurrentURL); + +	mAddressCombo->remove( mCurrentURL ); +	mAddressCombo->add( mCurrentURL ); +	mAddressCombo->selectByValue( mCurrentURL ); +} + +void LLFloaterWebContent::onClickForward() +{ +	mWebBrowser->navigateForward(); +} + +void LLFloaterWebContent::onClickBack() +{ +	mWebBrowser->navigateBack(); +} + +void LLFloaterWebContent::onClickReload() +{ + +	if( mWebBrowser->getMediaPlugin() ) +	{ +		bool ignore_cache = true; +		mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache ); +	} +	else +	{ +		mWebBrowser->navigateTo(mCurrentURL); +	} +} + +void LLFloaterWebContent::onClickStop() +{ +	if( mWebBrowser->getMediaPlugin() ) +		mWebBrowser->getMediaPlugin()->browse_stop(); + +	// still should happen when we catch the navigate complete event +	// but sometimes (don't know why) that event isn't sent from Qt +	// and we getto a point where the stop button stays active. +	getChildView("reload")->setVisible( true ); +	getChildView("stop")->setVisible( false ); +} + +void LLFloaterWebContent::onEnterAddress() +{ +	// make sure there is at least something there. +	// (perhaps this test should be for minimum length of a URL) +	std::string url = mAddressCombo->getValue().asString(); +	if ( url.length() > 0 ) +	{ +		mWebBrowser->navigateTo( url, "text/html"); +	}; +} + +void LLFloaterWebContent::onPopExternal() +{ +	// make sure there is at least something there. +	// (perhaps this test should be for minimum length of a URL) +	std::string url = mAddressCombo->getValue().asString(); +	if ( url.length() > 0 ) +	{ +		LLWeb::loadURLExternal( url ); +	}; +} diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h index 001d822ada..ecc7e970d8 100644 --- a/indra/newview/llfloaterwebcontent.h +++ b/indra/newview/llfloaterwebcontent.h @@ -1,82 +1,82 @@ -/**
 - * @file llfloaterwebcontent.h
 - * @brief floater for displaying web content - e.g. profiles and search (eventually)
 - *
 - * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 - * Second Life Viewer Source Code
 - * Copyright (C) 2010, Linden Research, Inc.
 - *
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Lesser General Public
 - * License as published by the Free Software Foundation;
 - * version 2.1 of the License only.
 - *
 - * This library is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Lesser General Public License for more details.
 - *
 - * You should have received a copy of the GNU Lesser General Public
 - * License along with this library; if not, write to the Free Software
 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 - *
 - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 - * $/LicenseInfo$
 - */
 -
 -#ifndef LL_LLFLOATERWEBCONTENT_H
 -#define LL_LLFLOATERWEBCONTENT_H
 -
 -#include "llfloater.h"
 -#include "llmediactrl.h"
 -
 -class LLMediaCtrl;
 -class LLComboBox;
 -class LLTextBox;
 -class LLProgressBar;
 -class LLIconCtrl;
 -
 -class LLFloaterWebContent :
 -	public LLFloater,
 -	public LLViewerMediaObserver
 -{
 -public:
 -    LOG_CLASS(LLFloaterWebContent);
 -	LLFloaterWebContent(const LLSD& key);
 -
 +/** + * @file llfloaterwebcontent.h + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERWEBCONTENT_H +#define LL_LLFLOATERWEBCONTENT_H + +#include "llfloater.h" +#include "llmediactrl.h" + +class LLMediaCtrl; +class LLComboBox; +class LLTextBox; +class LLProgressBar; +class LLIconCtrl; + +class LLFloaterWebContent : +	public LLFloater, +	public LLViewerMediaObserver +{ +public: +    LOG_CLASS(LLFloaterWebContent); +	LLFloaterWebContent(const LLSD& key); +  	void initializeURLHistory(); -
 -	static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null);
 -
 -	static void closeRequest(const std::string &uuid);
 -	static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height);
 -	void geometryChanged(S32 x, S32 y, S32 width, S32 height);
 -
 -	/* virtual */ BOOL postBuild();
 -	/* virtual */ void onClose(bool app_quitting);
 -	/* virtual */ void draw();
 -
 -	// inherited from LLViewerMediaObserver
 -	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event);
 -
 -	void onClickBack();
 -	void onClickForward();
 -	void onClickReload();
 -	void onClickStop();
 -	void onEnterAddress();
 -	void onPopExternal();
 -
 -private:
 -	void open_media(const std::string& media_url, const std::string& target);
 -	void set_current_url(const std::string& url);
 -
 -	LLMediaCtrl* mWebBrowser;
 -	LLComboBox* mAddressCombo;
 -	LLIconCtrl *mSecureLockIcon;
 -	LLTextBox* mStatusBarText;
 -	LLProgressBar* mStatusBarProgress;
 -	std::string mCurrentURL;
 -	std::string mUUID;
 -};
 -
 -#endif  // LL_LLFLOATERWEBCONTENT_H
 + +	static void create(const std::string &url, const std::string& target, const std::string& uuid = LLStringUtil::null); + +	static void closeRequest(const std::string &uuid); +	static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height); +	void geometryChanged(S32 x, S32 y, S32 width, S32 height); + +	/* virtual */ BOOL postBuild(); +	/* virtual */ void onClose(bool app_quitting); +	/* virtual */ void draw(); + +	// inherited from LLViewerMediaObserver +	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + +	void onClickBack(); +	void onClickForward(); +	void onClickReload(); +	void onClickStop(); +	void onEnterAddress(); +	void onPopExternal(); + +private: +	void open_media(const std::string& media_url, const std::string& target); +	void set_current_url(const std::string& url); + +	LLMediaCtrl* mWebBrowser; +	LLComboBox* mAddressCombo; +	LLIconCtrl *mSecureLockIcon; +	LLTextBox* mStatusBarText; +	LLProgressBar* mStatusBarProgress; +	std::string mCurrentURL; +	std::string mUUID; +}; + +#endif  // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 62ba746a02..b3b1ce5743 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -1854,31 +1854,9 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )  	{  		if (mCallbackRegistrar)  			mCallbackRegistrar->pushScope(); -		//menu->empty(); -		const LLView::child_list_t *list = menu->getChildList(); -		LLView::child_list_t::const_iterator menu_itor; -		for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) -		{ -			(*menu_itor)->setVisible(FALSE); -			(*menu_itor)->pushVisible(TRUE); -			(*menu_itor)->setEnabled(TRUE); -		} -		 -		// Successively filter out invalid options - -		U32 flags = FIRST_SELECTED_ITEM; -		for (selected_items_t::iterator item_itor = mSelectedItems.begin();  -			 item_itor != mSelectedItems.end();  -			 ++item_itor) -		{ -			LLFolderViewItem* selected_item = (*item_itor); -			selected_item->buildContextMenu(*menu, flags); -			flags = 0x0; -		} +		updateMenuOptions(menu); -		addNoOptions(menu); -  		menu->updateParent(LLMenuGL::sMenuContainer);  		LLMenuGL::showPopup(this, menu, x, y);  		if (mCallbackRegistrar) @@ -2365,6 +2343,45 @@ void LLFolderView::updateRenamerPosition()  	}  } +// Update visibility and availability (i.e. enabled/disabled) of context menu items. +void LLFolderView::updateMenuOptions(LLMenuGL* menu) +{ +	const LLView::child_list_t *list = menu->getChildList(); + +	LLView::child_list_t::const_iterator menu_itor; +	for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor) +	{ +		(*menu_itor)->setVisible(FALSE); +		(*menu_itor)->pushVisible(TRUE); +		(*menu_itor)->setEnabled(TRUE); +	} + +	// Successively filter out invalid options + +	U32 flags = FIRST_SELECTED_ITEM; +	for (selected_items_t::iterator item_itor = mSelectedItems.begin(); +			item_itor != mSelectedItems.end(); +			++item_itor) +	{ +		LLFolderViewItem* selected_item = (*item_itor); +		selected_item->buildContextMenu(*menu, flags); +		flags = 0x0; +	} + +	addNoOptions(menu); +} + +// Refresh the context menu (that is already shown). +void LLFolderView::updateMenu() +{ +	LLMenuGL* menu = (LLMenuGL*)mPopupMenuHandle.get(); +	if (menu && menu->getVisible()) +	{ +		updateMenuOptions(menu); +		menu->needsArrange(); // update menu height if needed +	} +} +  bool LLFolderView::selectFirstItem()  {  	for (folders_t::iterator iter = mFolders.begin(); diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index afaac86b04..210ba9eb3c 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -269,7 +269,10 @@ public:  	virtual S32	notify(const LLSD& info) ;  	bool useLabelSuffix() { return mUseLabelSuffix; } +	void updateMenu(); +  private: +	void updateMenuOptions(LLMenuGL* menu);  	void updateRenamerPosition();  protected: diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index e765a8da2f..a15776c207 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -100,7 +100,7 @@ public:  		void onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name); -		void onAdHocNameCache(const LLAvatarName& av_name);
 +		void onAdHocNameCache(const LLAvatarName& av_name);  		//*TODO make private  		static std::string generateHash(const std::set<LLUUID>& sorted_uuids); diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 4ad1934264..17d0b0ffbb 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -717,7 +717,7 @@ void LLInspectAvatar::onClickShare()  void LLInspectAvatar::onToggleMute()  { -	LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT); +	LLMute mute(mAvatarID, mAvatarName.mDisplayName, LLMute::AGENT);  	if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName))  	{ diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index ef4774a06d..e22363c2f6 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -393,18 +393,22 @@ void LLInventoryFilter::setFilterUUID(const LLUUID& object_id)  void LLInventoryFilter::setFilterSubString(const std::string& string)  { -	if (mFilterSubString != string) +	std::string filter_sub_string_new = string; +	mFilterSubStringOrig = string; +	LLStringUtil::trimHead(filter_sub_string_new); +	LLStringUtil::toUpper(filter_sub_string_new); + +	if (mFilterSubString != filter_sub_string_new)  	{  		// hitting BACKSPACE, for example -		const BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string); +		const BOOL less_restrictive = mFilterSubString.size() >= filter_sub_string_new.size() +			&& !mFilterSubString.substr(0, filter_sub_string_new.size()).compare(filter_sub_string_new);  		// appending new characters -		const BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString); +		const BOOL more_restrictive = mFilterSubString.size() < filter_sub_string_new.size() +			&& !filter_sub_string_new.substr(0, mFilterSubString.size()).compare(mFilterSubString); -		mFilterSubStringOrig = string; -		LLStringUtil::trimHead(mFilterSubStringOrig); -		mFilterSubString = mFilterSubStringOrig; -		LLStringUtil::toUpper(mFilterSubString); +		mFilterSubString = filter_sub_string_new;  		if (less_restrictive)  		{  			setModified(FILTER_LESS_RESTRICTIVE); diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index ef20869114..61d0a150b7 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -686,6 +686,12 @@ bool LLFindWearablesEx::operator()(LLInventoryCategory* cat, LLInventoryItem* it  		return false;  	} +	// Skip broken links. +	if (vitem->getIsBrokenLink()) +	{ +		return false; +	} +  	return (bool) get_is_item_worn(item->getUUID()) == mIsWorn;  } diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index eab8f187a7..570e48d526 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -181,7 +181,7 @@ void LLInventoryModelBackgroundFetch::backgroundFetch()  	if (mBackgroundFetchActive && gAgent.getRegion())  	{  		// If we'll be using the capability, we'll be sending batches and the background thing isn't as important. -		std::string url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents");    +		std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents2");     		if (!url.empty())   		{  			bulkFetch(url); @@ -604,7 +604,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch(std::string url)  		}  		if (body_lib["folders"].size())  		{ -			std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents"); +			std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2");  			LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(body_lib, recursive_cats);  			LLHTTPClient::post(url_lib, body_lib, fetcher, 300.0); diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 91ff8c7867..0fd4b2bee5 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -203,8 +203,8 @@ void fetch_items_from_llsd(const LLSD& items_llsd)  {  	if (!items_llsd.size() || gDisconnected) return;  	LLSD body; -	body[0]["cap_name"] = "FetchInventory"; -	body[1]["cap_name"] = "FetchLib"; +	body[0]["cap_name"] = "FetchInventory2"; +	body[1]["cap_name"] = "FetchLib2";  	for (S32 i=0; i<items_llsd.size();i++)  	{  		if (items_llsd[i]["owner_id"].asString() == gAgent.getID().asString()) diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 5a9d1524f3..1dcb91ad4d 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -60,6 +60,7 @@ static const LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER;  //  // Bridge to support knowing when the inventory has changed.  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  class LLInventoryPanelObserver : public LLInventoryObserver  {  public: @@ -73,9 +74,57 @@ protected:  	LLInventoryPanel* mIP;  }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInvPanelComplObserver +// +// Calls specified callback when all specified items become complete. +// +// Usage: +// observer = new LLInvPanelComplObserver(boost::bind(onComplete)); +// inventory->addObserver(observer); +// observer->reset(); // (optional) +// observer->watchItem(incomplete_item1_id); +// observer->watchItem(incomplete_item2_id); +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInvPanelComplObserver : public LLInventoryCompletionObserver +{ +public: +	typedef boost::function<void()> callback_t; + +	LLInvPanelComplObserver(callback_t cb) +	:	mCallback(cb) +	{ +	} + +	void reset(); + +private: +	/*virtual*/ void done(); + +	/// Called when all the items are complete. +	callback_t	mCallback; +}; + +void LLInvPanelComplObserver::reset() +{ +	mIncomplete.clear(); +	mComplete.clear(); +} + +void LLInvPanelComplObserver::done() +{ +	mCallback(); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryPanel +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +  LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :	  	LLPanel(p),  	mInventoryObserver(NULL), +	mCompletionObserver(NULL),  	mFolderRoot(NULL),  	mScroller(NULL),  	mSortOrderSetting(p.sort_order_setting), @@ -152,6 +201,9 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)  	mInventoryObserver = new LLInventoryPanelObserver(this);  	mInventory->addObserver(mInventoryObserver); +	mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); +	mInventory->addObserver(mCompletionObserver); +  	// Build view of inventory if we need default full hierarchy and inventory ready,  	// otherwise wait for idle callback.  	if (mBuildDefaultHierarchy && mInventory->isInventoryUsable() && !mViewsInitialized) @@ -189,7 +241,10 @@ LLInventoryPanel::~LLInventoryPanel()  	// LLView destructor will take care of the sub-views.  	mInventory->removeObserver(mInventoryObserver); +	mInventory->removeObserver(mCompletionObserver);  	delete mInventoryObserver; +	delete mCompletionObserver; +  	mScroller = NULL;  } @@ -654,6 +709,11 @@ void LLInventoryPanel::openStartFolderOrMyInventory()  	}  } +void LLInventoryPanel::onItemsCompletion() +{ +	if (mFolderRoot) mFolderRoot->updateMenu(); +} +  void LLInventoryPanel::openSelected()  {  	LLFolderViewItem* folder_item = mFolderRoot->getCurSelectedItem(); @@ -757,6 +817,19 @@ void LLInventoryPanel::clearSelection()  void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& items, BOOL user_action)  { +	// Schedule updating the folder view context menu when all selected items become complete (STORM-373). +	mCompletionObserver->reset(); +	for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(); it != items.end(); ++it) +	{ +		LLUUID id = (*it)->getListener()->getUUID(); +		LLViewerInventoryItem* inv_item = mInventory->getItem(id); + +		if (inv_item && !inv_item->isFinished()) +		{ +			mCompletionObserver->watchItem(id); +		} +	} +  	LLFolderView* fv = getRootFolder();  	if (fv->needsAutoRename()) // auto-selecting a new user-created asset and preparing to rename  	{ diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 6545fc0d5e..9da9f7d8ba 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -52,6 +52,7 @@ class LLIconCtrl;  class LLSaveFolderState;  class LLFilterEditor;  class LLTabContainer; +class LLInvPanelComplObserver;  class LLInventoryPanel : public LLPanel  { @@ -167,9 +168,11 @@ public:  protected:  	void openStartFolderOrMyInventory(); // open the first level of inventory +	void onItemsCompletion();			// called when selected items are complete  	LLInventoryModel*			mInventory;  	LLInventoryObserver*		mInventoryObserver; +	LLInvPanelComplObserver*	mCompletionObserver;  	BOOL 						mAllowMultiSelect;  	BOOL 						mShowItemLinkOverlays; // Shows link graphic over inventory item icons diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 1527f8f4c9..55164f6094 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -547,6 +547,10 @@ void LLLocationInputCtrl::onFocusLost()  {  	LLUICtrl::onFocusLost();  	refreshLocation(); + +	// Setting cursor to 0  to show the left edge of the text. See STORM-370. +	mTextEntry->setCursor(0); +  	if(mTextEntry->hasSelection()){  		mTextEntry->deselect();  	} diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 0121bbb1ed..9adf374c71 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -89,15 +89,15 @@ const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+   */  const static boost::regex NAME_AND_TEXT("([^:]+[:]{1})?(\\s*)(.*)"); -/**
 - * These are recognizers for matching the names of ad-hoc conferences when generating the log file name
 - * On invited side, an ad-hoc is named like "<first name> <last name> Conference 2010/11/19 03:43 f0f4"
 - * On initiating side, an ad-hoc is named like Ad-hoc Conference hash<hash>"
 - * If the naming system for ad-hoc conferences are change in LLIMModel::LLIMSession::buildHistoryFileName()
 - * then these definition need to be adjusted as well.
 - */
 -const static boost::regex INBOUND_CONFERENCE("^[a-zA-Z]{1,31} [a-zA-Z]{1,31} Conference [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [0-9a-f]{4}");
 -const static boost::regex OUTBOUND_CONFERENCE("^Ad-hoc Conference hash[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");
 +/** + * These are recognizers for matching the names of ad-hoc conferences when generating the log file name + * On invited side, an ad-hoc is named like "<first name> <last name> Conference 2010/11/19 03:43 f0f4" + * On initiating side, an ad-hoc is named like Ad-hoc Conference hash<hash>" + * If the naming system for ad-hoc conferences are change in LLIMModel::LLIMSession::buildHistoryFileName() + * then these definition need to be adjusted as well. + */ +const static boost::regex INBOUND_CONFERENCE("^[a-zA-Z]{1,31} [a-zA-Z]{1,31} Conference [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2} [0-9a-f]{4}"); +const static boost::regex OUTBOUND_CONFERENCE("^Ad-hoc Conference hash[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}");  //is used to parse complex object names like "Xstreet SL Terminal v2.2.5 st"  const static std::string NAME_TEXT_DIVIDER(": "); diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index efb2e9c0fd..33e051bfab 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -472,7 +472,6 @@ LLLoginInstance::LLLoginInstance() :  	mLoginModule(new LLLogin()),  	mNotifications(NULL),  	mLoginState("offline"), -	mUserInteraction(true),  	mSkipOptionalUpdate(false),  	mAttemptComplete(false),  	mTransferRate(0.0f), @@ -641,64 +640,57 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)  	LLSD response = event["data"];  	std::string reason_response = response["reason"].asString();  	std::string message_response = response["message"].asString(); -	if(mUserInteraction) +	// For the cases of critical message or TOS agreement, +	// start the TOS dialog. The dialog response will be handled +	// by the LLLoginInstance::handleTOSResponse() callback. +	// The callback intiates the login attempt next step, either  +	// to reconnect or to end the attempt in failure. +	if(reason_response == "tos")  	{ -		// For the cases of critical message or TOS agreement, -		// start the TOS dialog. The dialog response will be handled -		// by the LLLoginInstance::handleTOSResponse() callback. -		// The callback intiates the login attempt next step, either  -		// to reconnect or to end the attempt in failure. -		if(reason_response == "tos") -		{ -			LLSD data(LLSD::emptyMap()); -			data["message"] = message_response; -			data["reply_pump"] = TOS_REPLY_PUMP; -			gViewerWindow->setShowProgress(FALSE); -			LLFloaterReg::showInstance("message_tos", data); -			LLEventPumps::instance().obtain(TOS_REPLY_PUMP) -				.listen(TOS_LISTENER_NAME, -						boost::bind(&LLLoginInstance::handleTOSResponse,  -									this, _1, "agree_to_tos")); -		} -		else if(reason_response == "critical") -		{ -			LLSD data(LLSD::emptyMap()); -			data["message"] = message_response; -			data["reply_pump"] = TOS_REPLY_PUMP; -			if(response.has("error_code")) -			{ -				data["error_code"] = response["error_code"]; -			} -			if(response.has("certificate")) -			{ -				data["certificate"] = response["certificate"]; -			} -			 -			gViewerWindow->setShowProgress(FALSE); -			LLFloaterReg::showInstance("message_critical", data); -			LLEventPumps::instance().obtain(TOS_REPLY_PUMP) -				.listen(TOS_LISTENER_NAME, -						boost::bind(&LLLoginInstance::handleTOSResponse,  -									this, _1, "read_critical")); -		} -		else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate")) +		LLSD data(LLSD::emptyMap()); +		data["message"] = message_response; +		data["reply_pump"] = TOS_REPLY_PUMP; +		gViewerWindow->setShowProgress(FALSE); +		LLFloaterReg::showInstance("message_tos", data); +		LLEventPumps::instance().obtain(TOS_REPLY_PUMP) +			.listen(TOS_LISTENER_NAME, +					boost::bind(&LLLoginInstance::handleTOSResponse,  +								this, _1, "agree_to_tos")); +	} +	else if(reason_response == "critical") +	{ +		LLSD data(LLSD::emptyMap()); +		data["message"] = message_response; +		data["reply_pump"] = TOS_REPLY_PUMP; +		if(response.has("error_code"))  		{ -			gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); -			updateApp(true, message_response); +			data["error_code"] = response["error_code"];  		} -		else if(reason_response == "optional") +		if(response.has("certificate"))  		{ -			updateApp(false, message_response); +			data["certificate"] = response["certificate"];  		} -		else -		{	 -			attemptComplete(); -		}	 +		 +		gViewerWindow->setShowProgress(FALSE); +		LLFloaterReg::showInstance("message_critical", data); +		LLEventPumps::instance().obtain(TOS_REPLY_PUMP) +			.listen(TOS_LISTENER_NAME, +					boost::bind(&LLLoginInstance::handleTOSResponse,  +								this, _1, "read_critical"));  	} -	else // no user interaction +	else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate"))  	{ -		attemptComplete(); +		gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); +		updateApp(true, message_response); +	} +	else if(reason_response == "optional") +	{ +		updateApp(false, message_response);  	} +	else +	{	 +		attemptComplete(); +	}	  }  void LLLoginInstance::handleLoginSuccess(const LLSD& event) diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index b872d7d1b1..8b53431219 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -61,12 +61,6 @@ public:  	// Only valid when authSuccess == true.  	const F64 getLastTransferRateBPS() { return mTransferRate; } -		// Set whether this class will drive user interaction. -	// If not, login failures like 'need tos agreement' will  -	// end the login attempt. -	void setUserInteraction(bool state) { mUserInteraction = state; }  -	bool getUserInteraction() { return mUserInteraction; } -  	// Whether to tell login to skip optional update request.  	// False by default.  	void setSkipOptionalUpdate(bool state) { mSkipOptionalUpdate = state; } @@ -100,7 +94,6 @@ private:  	std::string mLoginState;  	LLSD mRequestData;  	LLSD mResponseData; -	bool mUserInteraction;   	bool mSkipOptionalUpdate;  	bool mAttemptComplete;  	F64 mTransferRate; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 90ed8b9e58..4a74b7925c 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -569,6 +569,7 @@ static void update_color_swatch_ctrl(LLPanelEditWearable* self, LLPanel* panel,  	if (color_swatch_ctrl)  	{  		color_swatch_ctrl->set(self->getWearable()->getClothesColor(entry->mTextureIndex)); +		color_swatch_ctrl->closeFloaterColorPicker();  	}  } diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index d1362d7922..3dbc637318 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1843,7 +1843,8 @@ bool LLPanelGroupRolesSubTab::apply(std::string& mesg)  {  	lldebugs << "LLPanelGroupRolesSubTab::apply()" << llendl; -	saveRoleChanges(); +	saveRoleChanges(true); +  	LLGroupMgr::getInstance()->sendGroupRoleChanges(mGroupID);  	notifyObservers(); @@ -2022,7 +2023,7 @@ void LLPanelGroupRolesSubTab::handleRoleSelect()  		return;  	} -	saveRoleChanges(); +	saveRoleChanges(false);  	// Check if there is anything selected.  	LLScrollListItem* item = mRolesList->getFirstSelected(); @@ -2385,7 +2386,7 @@ void LLPanelGroupRolesSubTab::handleDeleteRole()  	notifyObservers();  } -void LLPanelGroupRolesSubTab::saveRoleChanges() +void LLPanelGroupRolesSubTab::saveRoleChanges(bool select_saved_role)  {  	LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID); @@ -2400,13 +2401,23 @@ void LLPanelGroupRolesSubTab::saveRoleChanges()  		rd.mRoleDescription = mRoleDescription->getText();  		rd.mRoleTitle = mRoleTitle->getText(); +		S32 role_members_count = 0; +		if (mSelectedRole.isNull()) +		{ +			role_members_count = gdatap->mMemberCount; +		} +		else if(LLGroupRoleData* grd = get_ptr_in_map(gdatap->mRoles, mSelectedRole)) +		{ +			role_members_count = grd->getTotalMembersInRole(); +		} +  		gdatap->setRoleData(mSelectedRole,rd);  		mRolesList->deleteSingleItem(mRolesList->getItemIndex(mSelectedRole)); -		LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,0); +		LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,role_members_count);  		LLScrollListItem* item = mRolesList->addElement(row, ADD_BOTTOM, this); -		item->setSelected(TRUE); +		item->setSelected(select_saved_role);  		mHasRoleChange = FALSE;  	} diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 270259c16f..a55e264150 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -257,7 +257,7 @@ public:  	static void onDeleteRole(void*);  	void handleDeleteRole(); -	void saveRoleChanges(); +	void saveRoleChanges(bool select_saved_role);  	virtual void setGroupID(const LLUUID& id);  protected: diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index e8c8273a9d..80f6862169 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -71,6 +71,7 @@ static void collapse_all_folders(LLFolderView* root_folder);  static void expand_all_folders(LLFolderView* root_folder);  static bool has_expanded_folders(LLFolderView* root_folder);  static bool has_collapsed_folders(LLFolderView* root_folder); +static void toggle_restore_menu(LLMenuGL* menu, BOOL visible, BOOL enabled);  /**   * Functor counting expanded and collapsed folders in folder view tree to know @@ -708,6 +709,9 @@ void LLLandmarksPanel::initListCommandsHandlers()  	mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());  	mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_place_add_button.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	mGearLandmarkMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); +	mGearFolderMenu->setVisibilityChangeCallback(boost::bind(&LLLandmarksPanel::onMenuVisibilityChange, this, _1, _2)); +  	mListCommands->childSetAction(ADD_BUTTON_NAME, boost::bind(&LLLandmarksPanel::showActionMenu, this, mMenuAdd, ADD_BUTTON_NAME));  } @@ -1079,6 +1083,60 @@ void LLLandmarksPanel::onCustomAction(const LLSD& userdata)  	{  		doActionOnCurSelectedLandmark(boost::bind(&LLLandmarksPanel::doCreatePick, this, _1));  	} +	else if ("restore" == command_name && mCurrentSelectedList) +	{ +		mCurrentSelectedList->doToSelected(userdata); +	} +} + +void LLLandmarksPanel::onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param) +{ +	bool new_visibility = param["visibility"].asBoolean(); + +	// We don't have to update items visibility if the menu is hiding. +	if (!new_visibility) return; + +	BOOL are_any_items_in_trash = FALSE; +	BOOL are_all_items_in_trash = TRUE; + +	LLFolderView* root_folder_view = mCurrentSelectedList ? mCurrentSelectedList->getRootFolder() : NULL; +	if(root_folder_view) +	{ +		const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + +		std::set<LLUUID> selected_uuids = root_folder_view->getSelectionList(); + +		// Iterate through selected items to find out if any of these items are in Trash +		// or all the items are in Trash category. +		for (std::set<LLUUID>::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) +		{ +			LLFolderViewItem* item = root_folder_view->getItemByID(*iter); + +			// If no item is found it might be a folder id. +			if (!item) +			{ +				item = root_folder_view->getFolderByID(*iter); +			} +			if (!item) continue; + +			LLFolderViewEventListener* listenerp = item->getListener(); +			if(!listenerp) continue; + +			// Trash category itself should not be included because it can't be +			// actually restored from trash. +			are_all_items_in_trash &= listenerp->isItemInTrash() && *iter != trash_id; + +			// If there are any selected items in Trash including the Trash category itself +			// we show "Restore Item" in context menu and hide other irrelevant items. +			are_any_items_in_trash |= listenerp->isItemInTrash(); +		} +	} + +	// Display "Restore Item" menu entry if at least one of the selected items +	// is in Trash or the Trash category itself is among selected items. +	// Hide other menu entries in this case. +	// Enable this menu entry only if all selected items are in the Trash category. +	toggle_restore_menu((LLMenuGL*)ctrl, are_any_items_in_trash, are_all_items_in_trash);  }  /* @@ -1414,4 +1472,31 @@ static bool has_collapsed_folders(LLFolderView* root_folder)  	return true;  } + +// Displays "Restore Item" context menu entry while hiding +// all other entries or vice versa. +// Sets "Restore Item" enabled state. +void toggle_restore_menu(LLMenuGL *menu, BOOL visible, BOOL enabled) +{ +	if (!menu) return; + +	const LLView::child_list_t *list = menu->getChildList(); +	for (LLView::child_list_t::const_iterator itor = list->begin(); +		 itor != list->end(); +		 ++itor) +	{ +		LLView *menu_item = (*itor); +		std::string name = menu_item->getName(); + +		if ("restore_item" == name) +		{ +			menu_item->setVisible(visible); +			menu_item->setEnabled(enabled); +		} +		else +		{ +			menu_item->setVisible(!visible); +		} +	} +}  // EOF diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 8dcbca0440..b2f4e92473 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -129,6 +129,14 @@ private:  	void onCustomAction(const LLSD& command_name);  	/** +	 * Updates context menu depending on the selected items location. +	 * +	 * For items in Trash category the menu includes the "Restore Item" +	 * context menu entry. +	 */ +	void onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param); + +	/**  	 * Determines if an item can be modified via context/gear menu.  	 *  	 * It validates Places Landmarks rules first. And then LLFolderView permissions. diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index fcc67d6840..14e39f2c48 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -958,7 +958,7 @@ void LLPanelNearByMedia::onAdvancedButtonClick()  void LLPanelNearByMedia::onMoreLess()  { -	bool is_more = getChild<LLUICtrl>("more_btn")->getVisible(); +	bool is_more = getChild<LLButton>("more_btn")->getToggleState();  	mNearbyMediaPanel->setVisible(is_more);  	// enable resizing when expanded @@ -969,8 +969,7 @@ void LLPanelNearByMedia::onMoreLess()  	setShape(new_rect); -	getChild<LLUICtrl>("more_btn")->setVisible(!is_more); -	getChild<LLUICtrl>("less_btn")->setVisible(is_more); +	getChild<LLUICtrl>("more_btn")->setVisible(true);  }  void LLPanelNearByMedia::updateControls() diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 211b9cf4b1..0b6267c9e6 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -766,22 +766,12 @@ BOOL LLTaskCategoryBridge::startDrag(EDragAndDropType* type, LLUUID* id) const  		LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID());  		if(object)  		{ -			const LLInventoryItem *inv = dynamic_cast<LLInventoryItem*>(object->getInventoryObject(mUUID)); -			if (inv) +			const LLInventoryObject* cat = object->getInventoryObject(mUUID); +			if ( (cat) && (move_inv_category_world_to_agent(mUUID, LLUUID::null, FALSE)) )  			{ -				const LLPermissions& perm = inv->getPermissions(); -				bool can_copy = gAgent.allowOperation(PERM_COPY, perm, -														GP_OBJECT_MANIPULATE); -				if((can_copy && perm.allowTransferTo(gAgent.getID())) -				   || object->permYouOwner()) -//				   || gAgent.isGodlike()) - -				{ -					*type = LLViewerAssetType::lookupDragAndDropType(inv->getType()); - -					*id = inv->getUUID(); -					return TRUE; -				} +				*type = LLViewerAssetType::lookupDragAndDropType(cat->getType()); +				*id = mUUID; +				return TRUE;  			}  		}  	} diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 7657cccd4e..6cfb708112 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -273,6 +273,8 @@ void LLPreviewTexture::saveAs()  	mSaveFileName = file_picker.getFirstFile();  	mLoadingFullImage = TRUE;  	getWindow()->incBusyCount(); + +	mImage->forceToSaveRawImage(0) ;//re-fetch the raw image if the old one is removed.  	mImage->setLoadedCallback( LLPreviewTexture::onFileLoadedForSave,   								0, TRUE, FALSE, new LLUUID( mItemUUID ), &mCallbackTextureList );  } diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index e5ef51bdd1..3862dac340 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -33,6 +33,7 @@  #include "llpanel.h"  #include "llhttpclient.h"  #include "llsdserialize.h" +#include "llurlentry.h"  #include "llviewerregion.h"  #include "llview.h" @@ -168,6 +169,18 @@ void LLRemoteParcelInfoProcessor::processParcelInfoReply(LLMessageSystem* msg, v  	{  		observers.erase(*i);  	} + +	LLUrlEntryParcel::LLParcelData url_data; +	url_data.parcel_id = parcel_data.parcel_id; +	url_data.name = parcel_data.name; +	url_data.sim_name = parcel_data.sim_name; +	url_data.global_x = parcel_data.global_x; +	url_data.global_y = parcel_data.global_y; +	url_data.global_z = parcel_data.global_z; + +	// Pass the parcel data to LLUrlEntryParcel to render +	// human readable parcel name. +	LLUrlEntryParcel::processParcelInfo(url_data);  }  void LLRemoteParcelInfoProcessor::sendParcelInfoRequest(const LLUUID& parcel_id) diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 7478ed5f9a..65a9a493f6 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -235,7 +235,7 @@ public:  	{  		bool operator()(LLSelectNode* node);  	}; -	typedef boost::filter_iterator<is_root, list_t::iterator > valid_root_iterator; +	typedef boost::filter_iterator<is_valid_root, list_t::iterator > valid_root_iterator;  	valid_root_iterator valid_root_begin() { return valid_root_iterator(mList.begin(), mList.end()); }  	valid_root_iterator valid_root_end() { return valid_root_iterator(mList.end(), mList.end()); } diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index b316171604..363fe5f12b 100644 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -185,7 +185,7 @@ void LLSidepanelAppearance::onVisibilityChange(const LLSD &new_visibility)  {  	LLSD visibility;  	visibility["visible"] = new_visibility.asBoolean(); -	visibility["reset_accordion"] = true; +	visibility["reset_accordion"] = false;  	updateToVisibility(visibility);  } diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index aef665a35c..19d1bdee86 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -498,8 +498,8 @@ private:  LLSideTray::Params::Params()  :	collapsed("collapsed",false), -	tab_btn_image_normal("tab_btn_image",LLUI::getUIImage("sidebar_tab_left.tga")), -	tab_btn_image_selected("tab_btn_image_selected",LLUI::getUIImage("button_enabled_selected_32x128.tga")), +	tab_btn_image_normal("tab_btn_image",LLUI::getUIImage("taskpanel/TaskPanel_Tab_Off.png")), +	tab_btn_image_selected("tab_btn_image_selected",LLUI::getUIImage("taskpanel/TaskPanel_Tab_Selected.png")),  	default_button_width("tab_btn_width",32),  	default_button_height("tab_btn_height",32),  	default_button_margin("tab_btn_margin",0) diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index 960e72ee42..8adb8c30e0 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2578,6 +2578,49 @@ void renderCrossHairs(LLVector3 position, F32 size, LLColor4 color)  	gGL.end();  } +void renderUpdateType(LLDrawable* drawablep) +{ +	LLViewerObject* vobj = drawablep->getVObj(); +	if (!vobj || OUT_UNKNOWN == vobj->getLastUpdateType()) +	{ +		return; +	} +	LLGLEnable blend(GL_BLEND); +	switch (vobj->getLastUpdateType()) +	{ +	case OUT_FULL: +		glColor4f(0,1,0,0.5f); +		break; +	case OUT_TERSE_IMPROVED: +		glColor4f(0,1,1,0.5f); +		break; +	case OUT_FULL_COMPRESSED: +		if (vobj->getLastUpdateCached()) +		{ +			glColor4f(1,0,0,0.5f); +		} +		else +		{ +			glColor4f(1,1,0,0.5f); +		} +		break; +	case OUT_FULL_CACHED: +		glColor4f(0,0,1,0.5f); +		break; +	default: +		llwarns << "Unknown update_type " << vobj->getLastUpdateType() << llendl; +		break; +	}; +	S32 num_faces = drawablep->getNumFaces(); +	if (num_faces) +	{ +		for (S32 i = 0; i < num_faces; ++i) +		{ +			pushVerts(drawablep->getFace(i), LLVertexBuffer::MAP_VERTEX); +		} +	} +} +  void renderBoundingBox(LLDrawable* drawable, BOOL set_color = TRUE)  { @@ -3018,6 +3061,10 @@ public:  			{  				renderRaycast(drawable);  			} +			if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_UPDATE_TYPE)) +			{ +				renderUpdateType(drawable); +			}  			LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(drawable->getVObj().get()); @@ -3180,6 +3227,7 @@ void LLSpatialPartition::renderDebug()  									  LLPipeline::RENDER_DEBUG_OCCLUSION |  									  LLPipeline::RENDER_DEBUG_LIGHTS |  									  LLPipeline::RENDER_DEBUG_BATCH_SIZE | +									  LLPipeline::RENDER_DEBUG_UPDATE_TYPE |  									  LLPipeline::RENDER_DEBUG_BBOXES |  									  LLPipeline::RENDER_DEBUG_POINTS |  									  LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY | diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 611f9de2e6..0eac7d5e2a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -139,6 +139,7 @@  #include "lltrans.h"  #include "llui.h"  #include "llurldispatcher.h" +#include "llurlentry.h"  #include "llslurl.h"  #include "llurlhistory.h"  #include "llurlwhitelist.h" @@ -980,7 +981,6 @@ bool idle_startup()  			login->setSkipOptionalUpdate(true);  		} -		login->setUserInteraction(show_connect_box);  		login->setSerialNumber(LLAppViewer::instance()->getSerialNumber());  		login->setLastExecEvent(gLastExecEvent);  		login->setUpdaterLauncher(boost::bind(&LLAppViewer::launchUpdater, LLAppViewer::instance())); @@ -2882,9 +2882,17 @@ bool process_login_success_response()  	if(!text.empty()) gAgentID.set(text);  	gDebugInfo["AgentID"] = text; +	// Agent id needed for parcel info request in LLUrlEntryParcel +	// to resolve parcel name. +	LLUrlEntryParcel::setAgentID(gAgentID); +  	text = response["session_id"].asString();  	if(!text.empty()) gAgentSessionID.set(text);  	gDebugInfo["SessionID"] = text; + +	// Session id needed for parcel info request in LLUrlEntryParcel +	// to resolve parcel name. +	LLUrlEntryParcel::setSessionID(gAgentSessionID);  	text = response["secure_session_id"].asString();  	if(!text.empty()) gAgent.mSecureSessionID.set(text); diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 6a213309a0..f54214b95c 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -326,6 +326,7 @@ bool LLTextureCacheRemoteWorker::doRead()  	// First state / stage : find out if the file is local  	if (mState == INIT)  	{ +#if 0  		std::string filename = mCache->getLocalFileName(mID);	  		// Is it a JPEG2000 file?   		{ @@ -360,6 +361,11 @@ bool LLTextureCacheRemoteWorker::doRead()  		}  		// Determine the next stage: if we found a file, then LOCAL else CACHE  		mState = (local_size > 0 ? LOCAL : CACHE); + +		llassert_always(mState == CACHE) ; +#else +		mState = CACHE; +#endif  	}  	// Second state / stage : if the file is local, load it and leave @@ -1592,7 +1598,7 @@ void LLTextureCache::purgeTextures(bool validate)  	if (validate)  	{  		validate_idx = gSavedSettings.getU32("CacheValidateCounter"); -		U32 next_idx = (++validate_idx) % 256; +		U32 next_idx = (validate_idx + 1) % 256;  		gSavedSettings.setU32("CacheValidateCounter", next_idx);  		LL_DEBUGS("TextureCache") << "TEXTURE CACHE: Validating: " << validate_idx << LL_ENDL;  	} @@ -1858,8 +1864,22 @@ void LLTextureCache::removeCachedTexture(const LLUUID& id)  //called after mHeaderMutex is locked.  void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename)  { + 	bool file_maybe_exists = true;	// Always attempt to remove when idx is invalid. +  	if(idx >= 0) //valid entry  	{ +		if (entry.mBodySize == 0)	// Always attempt to remove when mBodySize > 0. +		{ +		  if (LLAPRFile::isExist(filename, getLocalAPRFilePool()))		// Sanity check. Shouldn't exist when body size is 0. +		  { +			  LL_WARNS("TextureCache") << "Entry has body size of zero but file " << filename << " exists. Deleting this file, too." << LL_ENDL; +		  } +		  else +		  { +			  file_maybe_exists = false; +		  } +		} +  		entry.mImageSize = -1;  		entry.mBodySize = 0;  		mHeaderIDMap.erase(entry.mID); @@ -1869,7 +1889,10 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename)  		mFreeList.insert(idx);	  	} -	LLAPRFile::remove(filename, getLocalAPRFilePool());		 +	if (file_maybe_exists) +	{ +		LLAPRFile::remove(filename, getLocalAPRFilePool());		 +	}  }  bool LLTextureCache::removeFromCache(const LLUUID& id) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 4f63abb152..5cdf1706e6 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1,3000 +1,3011 @@ -/**  - * @file lltexturefetch.cpp - * @brief Object which fetches textures from the cache and/or network - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - *  - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - *  - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * Lesser General Public License for more details. - *  - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA - *  - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include <iostream> -#include <map> - -#include "llstl.h" - -#include "lltexturefetch.h" - -#include "llcurl.h" -#include "lldir.h" -#include "llhttpclient.h" -#include "llhttpstatuscodes.h" -#include "llimage.h" -#include "llimagej2c.h" -#include "llimageworker.h" -#include "llworkerthread.h" -#include "message.h" - -#include "llagent.h" -#include "lltexturecache.h" -#include "llviewercontrol.h" -#include "llviewertexturelist.h" -#include "llviewertexture.h" -#include "llviewerregion.h" -#include "llviewerstats.h" -#include "llviewerassetstats.h" -#include "llworld.h" - -////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass -{ -	friend class LLTextureFetch; -	friend class HTTPGetResponder; -	 -private: -	class CacheReadResponder : public LLTextureCache::ReadResponder -	{ -	public: -		CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) -			: mFetcher(fetcher), mID(id) -		{ -			setImage(image); -		} -		virtual void completed(bool success) -		{ -			LLTextureFetchWorker* worker = mFetcher->getWorker(mID); -			if (worker) -			{ - 				worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal); -			} -		} -	private: -		LLTextureFetch* mFetcher; -		LLUUID mID; -	}; - -	class CacheWriteResponder : public LLTextureCache::WriteResponder -	{ -	public: -		CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) -			: mFetcher(fetcher), mID(id) -		{ -		} -		virtual void completed(bool success) -		{ -			LLTextureFetchWorker* worker = mFetcher->getWorker(mID); -			if (worker) -			{ -				worker->callbackCacheWrite(success); -			} -		} -	private: -		LLTextureFetch* mFetcher; -		LLUUID mID; -	}; -	 -	class DecodeResponder : public LLImageDecodeThread::Responder -	{ -	public: -		DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) -			: mFetcher(fetcher), mID(id), mWorker(worker) -		{ -		} -		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) -		{ -			LLTextureFetchWorker* worker = mFetcher->getWorker(mID); -			if (worker) -			{ - 				worker->callbackDecoded(success, raw, aux); -			} -		} -	private: -		LLTextureFetch* mFetcher; -		LLUUID mID; -		LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID) -	}; - -	struct Compare -	{ -		// lhs < rhs -		bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const -		{ -			// greater priority is "less" -			const F32 lpriority = lhs->mImagePriority; -			const F32 rpriority = rhs->mImagePriority; -			if (lpriority > rpriority) // higher priority -				return true; -			else if (lpriority < rpriority) -				return false; -			else -				return lhs < rhs; -		} -	}; -	 -public: -	/*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() -	/*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) -	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) - -	~LLTextureFetchWorker(); -	// void relese() { --mActiveCount; } - -	S32 callbackHttpGet(const LLChannelDescriptors& channels, -						 const LLIOPipe::buffer_ptr_t& buffer, -						 bool partial, bool success); -	void callbackCacheRead(bool success, LLImageFormatted* image, -						   S32 imagesize, BOOL islocal); -	void callbackCacheWrite(bool success); -	void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); -	 -	void setGetStatus(U32 status, const std::string& reason) -	{ -		LLMutexLock lock(&mWorkMutex); - -		mGetStatus = status; -		mGetReason = reason; -	} - -	void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; } -	bool getCanUseHTTP() const { return mCanUseHTTP; } - -	LLTextureFetch & getFetcher() { return *mFetcher; } -	 -protected: -	LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, -						 F32 priority, S32 discard, S32 size); - -private: -	/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) -	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) - -	void resetFormattedData(); -	 -	void setImagePriority(F32 priority); -	void setDesiredDiscard(S32 discard, S32 size); -	bool insertPacket(S32 index, U8* data, S32 size); -	void clearPackets(); -	void setupPacketData(); -	U32 calcWorkPriority(); -	void removeFromCache(); -	bool processSimulatorPackets(); -	bool writeToCacheComplete(); -	 -	void lockWorkMutex() { mWorkMutex.lock(); } -	void unlockWorkMutex() { mWorkMutex.unlock(); } - -private: -	enum e_state // mState -	{ -		// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) -		INVALID = 0, -		INIT, -		LOAD_FROM_TEXTURE_CACHE, -		CACHE_POST, -		LOAD_FROM_NETWORK, -		LOAD_FROM_SIMULATOR, -		SEND_HTTP_REQ, -		WAIT_HTTP_REQ, -		DECODE_IMAGE, -		DECODE_IMAGE_UPDATE, -		WRITE_TO_CACHE, -		WAIT_ON_WRITE, -		DONE -	}; -	enum e_request_state // mSentRequest -	{ -		UNSENT = 0, -		QUEUED = 1, -		SENT_SIM = 2 -	}; -	enum e_write_to_cache_state //mWriteToCacheState -	{ -		NOT_WRITE = 0, -		CAN_WRITE = 1, -		SHOULD_WRITE = 2 -	}; -	static const char* sStateDescs[]; -	e_state mState; -	e_write_to_cache_state mWriteToCacheState; -	LLTextureFetch* mFetcher; -	LLPointer<LLImageFormatted> mFormattedImage; -	LLPointer<LLImageRaw> mRawImage; -	LLPointer<LLImageRaw> mAuxImage; -	LLUUID mID; -	LLHost mHost; -	std::string mUrl; -	U8 mType; -	F32 mImagePriority; -	U32 mWorkPriority; -	F32 mRequestedPriority; -	S32 mDesiredDiscard; -	S32 mSimRequestedDiscard; -	S32 mRequestedDiscard; -	S32 mLoadedDiscard; -	S32 mDecodedDiscard; -	LLFrameTimer mRequestedTimer; -	LLFrameTimer mFetchTimer; -	LLTextureCache::handle_t mCacheReadHandle; -	LLTextureCache::handle_t mCacheWriteHandle; -	U8* mBuffer; -	S32 mBufferSize; -	S32 mRequestedSize; -	S32 mDesiredSize; -	S32 mFileSize; -	S32 mCachedSize;	 -	e_request_state mSentRequest; -	handle_t mDecodeHandle; -	BOOL mLoaded; -	BOOL mDecoded; -	BOOL mWritten; -	BOOL mNeedsAux; -	BOOL mHaveAllData; -	BOOL mInLocalCache; -	bool mCanUseHTTP ; -	bool mCanUseNET ; //can get from asset server. -	S32 mHTTPFailCount; -	S32 mRetryAttempt; -	S32 mActiveCount; -	U32 mGetStatus; -	std::string mGetReason; -	 -	// Work Data -	LLMutex mWorkMutex; -	struct PacketData -	{ -		PacketData(U8* data, S32 size) { mData = data; mSize = size; } -		~PacketData() { clearData(); } -		void clearData() { delete[] mData; mData = NULL; } -		U8* mData; -		U32 mSize; -	}; -	std::vector<PacketData*> mPackets; -	S32 mFirstPacket; -	S32 mLastPacket; -	U16 mTotalPackets; -	U8 mImageCodec; - -	LLViewerAssetStats::duration_t mMetricsStartTime; -}; - -////////////////////////////////////////////////////////////////////////////// - -class HTTPGetResponder : public LLCurl::Responder -{ -	LOG_CLASS(HTTPGetResponder); -public: -	HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) -		: mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) -	{ -	} -	~HTTPGetResponder() -	{ -	} - -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) -	{ -		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); -		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); -		static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - -		if (log_to_viewer_log || log_to_sim) -		{ -			mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); -			U64 timeNow = LLTimer::getTotalTime(); -			mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); -			mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); -			mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); -			mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); -		} - -		lldebugs << "HTTP COMPLETE: " << mID << llendl; -		LLTextureFetchWorker* worker = mFetcher->getWorker(mID); -		if (worker) -		{ -			bool success = false; -			bool partial = false; -			if (HTTP_OK <= status &&  status < HTTP_MULTIPLE_CHOICES) -			{ -				success = true; -				if (HTTP_PARTIAL_CONTENT == status) // partial information -				{ -					partial = true; -				} -			} - -			if (!success) -			{ -				worker->setGetStatus(status, reason); -// 				llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; -			} -			 -			S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success); -			 -			if(log_texture_traffic && data_size > 0) -			{ -				LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; -				if(tex) -				{ -					gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; -				} -			} - -			mFetcher->removeFromHTTPQueue(mID, data_size); - -			if (worker->mMetricsStartTime) -			{ -				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, -															  true, -															  LLImageBase::TYPE_AVATAR_BAKE == worker->mType, -															  LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); -				worker->mMetricsStartTime = 0; -			} -			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, -														 true, -														 LLImageBase::TYPE_AVATAR_BAKE == worker->mType); -		} -		else -		{ -			mFetcher->removeFromHTTPQueue(mID); - 			llwarns << "Worker not found: " << mID << llendl; -		} -	} - -	virtual bool followRedir() -	{ -		return mFollowRedir; -	} -	 -private: -	LLTextureFetch* mFetcher; -	LLUUID mID; -	U64 mStartTime; -	S32 mRequestedSize; -	U32 mOffset; -	bool mFollowRedir; -}; - -////////////////////////////////////////////////////////////////////////////// - -// Cross-thread messaging for asset metrics. - -/** - * @brief Base class for cross-thread requests made of the fetcher - * - * I believe the intent of the LLQueuedThread class was to - * have these operations derived from LLQueuedThread::QueuedRequest - * but the texture fetcher has elected to manage the queue - * in its own manner.  So these are free-standing objects which are - * managed in simple FIFO order on the mCommands queue of the - * LLTextureFetch object. - * - * What each represents is a simple command sent from an - * outside thread into the TextureFetch thread to be processed - * in order and in a timely fashion (though not an absolute - * higher priority than other operations of the thread). - * Each operation derives a new class from the base customizing - * members, constructors and the doWork() method to effect - * the command. - * - * The flow is one-directional.  There are two global instances - * of the LLViewerAssetStats collector, one for the main program's - * thread pointed to by gViewerAssetStatsMain and one for the - * TextureFetch thread pointed to by gViewerAssetStatsThread1. - * Common operations has each thread recording metrics events - * into the respective collector unconcerned with locking and - * the state of any other thread.  But when the agent moves into - * a different region or the metrics timer expires and a report - * needs to be sent back to the grid, messaging across threads - * is required to distribute data and perform global actions. - * In pseudo-UML, it looks like: - * - *                       Main                 Thread1 - *                        .                      . - *                        .                      . - *                     +-----+                   . - *                     | AM  |                   . - *                     +--+--+                   . - *      +-------+         |                      . - *      | Main  |      +--+--+                   . - *      |       |      | SRE |---.               . - *      | Stats |      +-----+    \              . - *      |       |         |        \  (uuid)  +-----+ - *      | Coll. |      +--+--+      `-------->| SR  | - *      +-------+      | MSC |                +--+--+ - *         | ^         +-----+                   | - *         | |  (uuid)  / .                   +-----+ (uuid) - *         |  `--------'  .                   | MSC |---------. - *         |              .                   +-----+         | - *         |           +-----+                   .            v - *         |           | TE  |                   .        +-------+ - *         |           +--+--+                   .        | Thd1  | - *         |              |                      .        |       | - *         |           +-----+                   .        | Stats | - *          `--------->| RSC |                   .        |       | - *                     +--+--+                   .        | Coll. | - *                        |                      .        +-------+ - *                     +--+--+                   .            | - *                     | SME |---.               .            | - *                     +-----+    \              .            | - *                        .        \ (clone)  +-----+         | - *                        .         `-------->| SM  |         | - *                        .                   +--+--+         | - *                        .                      |            | - *                        .                   +-----+         | - *                        .                   | RSC |<--------' - *                        .                   +-----+ - *                        .                      | - *                        .                   +-----+ - *                        .                   | CP  |--> HTTP POST - *                        .                   +-----+ - *                        .                      . - *                        .                      . - * - * - * Key: - * - * SRE - Set Region Enqueued.  Enqueue a 'Set Region' command in - *       the other thread providing the new UUID of the region. - *       TFReqSetRegion carries the data. - * SR  - Set Region.  New region UUID is sent to the thread-local - *       collector. - * SME - Send Metrics Enqueued.  Enqueue a 'Send Metrics' command - *       including an ownership transfer of a cloned LLViewerAssetStats. - *       TFReqSendMetrics carries the data. - * SM  - Send Metrics.  Global metrics reporting operation.  Takes - *       the cloned stats from the command, merges it with the - *       thread's local stats, converts to LLSD and sends it on - *       to the grid. - * AM  - Agent Moved.  Agent has completed some sort of move to a - *       new region. - * TE  - Timer Expired.  Metrics timer has expired (on the order - *       of 10 minutes). - * CP  - CURL Post - * MSC - Modify Stats Collector.  State change in the thread-local - *       collector.  Typically a region change which affects the - *       global pointers used to find the 'current stats'. - * RSC - Read Stats Collector.  Extract collector data cloning it - *       (i.e. deep copy) when necessary. - * - */ -class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest -{ -public: -	// Default ctors and assignment operator are correct. - -	virtual ~TFRequest() -		{} - -	// Patterned after QueuedRequest's method but expected behavior -	// is different.  Always expected to complete on the first call -	// and work dispatcher will assume the same and delete the -	// request after invocation. -	virtual bool doWork(LLTextureFetch * fetcher) = 0; -}; - -namespace  -{ - -/** - * @brief Implements a 'Set Region' cross-thread command. - * - * When an agent moves to a new region, subsequent metrics need - * to be binned into a new or existing stats collection in 1:1 - * relationship with the region.  We communicate this region - * change across the threads involved in the communication with - * this message. - * - * Corresponds to LLTextureFetch::commandSetRegion() - */ -class TFReqSetRegion : public LLTextureFetch::TFRequest -{ -public: -	TFReqSetRegion(U64 region_handle) -		: LLTextureFetch::TFRequest(), -		  mRegionHandle(region_handle) -		{} -	TFReqSetRegion & operator=(const TFReqSetRegion &);	// Not defined - -	virtual ~TFReqSetRegion() -		{} - -	virtual bool doWork(LLTextureFetch * fetcher); -		 -public: -	const U64 mRegionHandle; -}; - - -/** - * @brief Implements a 'Send Metrics' cross-thread command. - * - * This is the big operation.  The main thread gathers metrics - * for a period of minutes into LLViewerAssetStats and other - * objects then makes a snapshot of the data by cloning the - * collector.  This command transfers the clone, along with a few - * additional arguments (UUIDs), handing ownership to the - * TextureFetch thread.  It then merges its own data into the - * cloned copy, converts to LLSD and kicks off an HTTP POST of - * the resulting data to the currently active metrics collector. - * - * Corresponds to LLTextureFetch::commandSendMetrics() - */ -class TFReqSendMetrics : public LLTextureFetch::TFRequest -{ -public: -    /** -	 * Construct the 'Send Metrics' command to have the TextureFetch -	 * thread add and log metrics data. -	 * -	 * @param	caps_url		URL of a "ViewerMetrics" Caps target -	 *							to receive the data.  Does not have to -	 *							be associated with a particular region. -	 * -	 * @param	session_id		UUID of the agent's session. -	 * -	 * @param	agent_id		UUID of the agent.  (Being pure here...) -	 * -	 * @param	main_stats		Pointer to a clone of the main thread's -	 *							LLViewerAssetStats data.  Thread1 takes -	 *							ownership of the copy and disposes of it -	 *							when done. -	 */ -	TFReqSendMetrics(const std::string & caps_url, -					 const LLUUID & session_id, -					 const LLUUID & agent_id, -					 LLViewerAssetStats * main_stats) -		: LLTextureFetch::TFRequest(), -		  mCapsURL(caps_url), -		  mSessionID(session_id), -		  mAgentID(agent_id), -		  mMainStats(main_stats) -		{} -	TFReqSendMetrics & operator=(const TFReqSendMetrics &);	// Not defined - -	virtual ~TFReqSendMetrics(); - -	virtual bool doWork(LLTextureFetch * fetcher); -		 -public: -	const std::string mCapsURL; -	const LLUUID mSessionID; -	const LLUUID mAgentID; -	LLViewerAssetStats * mMainStats; -}; - -/* - * Examines the merged viewer metrics report and if found to be too long, - * will attempt to truncate it in some reasonable fashion. - * - * @param		max_regions		Limit of regions allowed in report. - * - * @param		metrics			Full, merged viewer metrics report. - * - * @returns		If data was truncated, returns true. - */ -bool truncate_viewer_metrics(int max_regions, LLSD & metrics); - -} // end of anonymous namespace - - -////////////////////////////////////////////////////////////////////////////// - -//static -const char* LLTextureFetchWorker::sStateDescs[] = { -	"INVALID", -	"INIT", -	"LOAD_FROM_TEXTURE_CACHE", -	"CACHE_POST", -	"LOAD_FROM_NETWORK", -	"LOAD_FROM_SIMULATOR", -	"SEND_HTTP_REQ", -	"WAIT_HTTP_REQ", -	"DECODE_IMAGE", -	"DECODE_IMAGE_UPDATE", -	"WRITE_TO_CACHE", -	"WAIT_ON_WRITE", -	"DONE", -}; - -// static -volatile bool LLTextureFetch::svMetricsDataBreak(true);	// Start with a data break - -// called from MAIN THREAD - -LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, -										   const std::string& url, // Optional URL -										   const LLUUID& id,	// Image UUID -										   const LLHost& host,	// Simulator host -										   F32 priority,		// Priority -										   S32 discard,			// Desired discard -										   S32 size)			// Desired size -	: LLWorkerClass(fetcher, "TextureFetch"), -	  mState(INIT), -	  mWriteToCacheState(NOT_WRITE), -	  mFetcher(fetcher), -	  mID(id), -	  mHost(host), -	  mUrl(url), -	  mImagePriority(priority), -	  mWorkPriority(0), -	  mRequestedPriority(0.f), -	  mDesiredDiscard(-1), -	  mSimRequestedDiscard(-1), -	  mRequestedDiscard(-1), -	  mLoadedDiscard(-1), -	  mDecodedDiscard(-1), -	  mCacheReadHandle(LLTextureCache::nullHandle()), -	  mCacheWriteHandle(LLTextureCache::nullHandle()), -	  mBuffer(NULL), -	  mBufferSize(0), -	  mRequestedSize(0), -	  mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), -	  mFileSize(0), -	  mCachedSize(0), -	  mLoaded(FALSE), -	  mSentRequest(UNSENT), -	  mDecodeHandle(0), -	  mDecoded(FALSE), -	  mWritten(FALSE), -	  mNeedsAux(FALSE), -	  mHaveAllData(FALSE), -	  mInLocalCache(FALSE), -	  mCanUseHTTP(true), -	  mHTTPFailCount(0), -	  mRetryAttempt(0), -	  mActiveCount(0), -	  mGetStatus(0), -	  mWorkMutex(NULL), -	  mFirstPacket(0), -	  mLastPacket(-1), -	  mTotalPackets(0), -	  mImageCodec(IMG_CODEC_INVALID), -	  mMetricsStartTime(0) -{ -	mCanUseNET = mUrl.empty() ; - -	calcWorkPriority(); -	mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; -// 	llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; -	if (!mFetcher->mDebugPause) -	{ -		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; -		addWork(0, work_priority ); -	} -	setDesiredDiscard(discard, size); -} - -LLTextureFetchWorker::~LLTextureFetchWorker() -{ -// 	llinfos << "Destroy: " << mID -// 			<< " Decoded=" << mDecodedDiscard -// 			<< " Requested=" << mRequestedDiscard -// 			<< " Desired=" << mDesiredDiscard << llendl; -	llassert_always(!haveWork()); -	lockWorkMutex(); -	if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) -	{ -		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); -	} -	if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) -	{ -		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); -	} -	mFormattedImage = NULL; -	clearPackets(); -	unlockWorkMutex(); -	mFetcher->removeFromHTTPQueue(mID); -} - -void LLTextureFetchWorker::clearPackets() -{ -	for_each(mPackets.begin(), mPackets.end(), DeletePointer()); -	mPackets.clear(); -	mTotalPackets = 0; -	mLastPacket = -1; -	mFirstPacket = 0; -} - -void LLTextureFetchWorker::setupPacketData() -{ -	S32 data_size = 0; -	if (mFormattedImage.notNull()) -	{ -		data_size = mFormattedImage->getDataSize(); -	} -	if (data_size > 0) -	{ -		// Only used for simulator requests -		mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; -		if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) -		{ -			llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; -			removeFromCache(); -			resetFormattedData(); -			clearPackets(); -		} -		else if (mFileSize > 0) -		{ -			mLastPacket = mFirstPacket-1; -			mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; -		} -		else -		{ -			// This file was cached using HTTP so we have to refetch the first packet -			resetFormattedData(); -			clearPackets(); -		} -	} -} - -U32 LLTextureFetchWorker::calcWorkPriority() -{ - 	//llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); -	static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority(); - -	mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE)); -	return mWorkPriority; -} - -// mWorkMutex is locked -void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) -{ -	bool prioritize = false; -	if (mDesiredDiscard != discard) -	{ -		if (!haveWork()) -		{ -			calcWorkPriority(); -			if (!mFetcher->mDebugPause) -			{ -				U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; -				addWork(0, work_priority); -			} -		} -		else if (mDesiredDiscard < discard) -		{ -			prioritize = true; -		} -		mDesiredDiscard = discard; -		mDesiredSize = size; -	} -	else if (size > mDesiredSize) -	{ -		mDesiredSize = size; -		prioritize = true; -	} -	mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); -	if ((prioritize && mState == INIT) || mState == DONE) -	{ -		mState = INIT; -		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; -		setPriority(work_priority); -	} -} - -void LLTextureFetchWorker::setImagePriority(F32 priority) -{ -// 	llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); -	F32 delta = fabs(priority - mImagePriority); -	if (delta > (mImagePriority * .05f) || mState == DONE) -	{ -		mImagePriority = priority; -		calcWorkPriority(); -		U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); -		setPriority(work_priority); -	} -} - -void LLTextureFetchWorker::resetFormattedData() -{ -	delete[] mBuffer; -	mBuffer = NULL; -	mBufferSize = 0; -	if (mFormattedImage.notNull()) -	{ -		mFormattedImage->deleteData(); -	} -	mHaveAllData = FALSE; -} - -// Called from MAIN thread -void LLTextureFetchWorker::startWork(S32 param) -{ -	llassert(mFormattedImage.isNull()); -} - -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() -bool LLTextureFetchWorker::doWork(S32 param) -{ -	LLMutexLock lock(&mWorkMutex); - -	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) -	{ -		if (mState < DECODE_IMAGE) -		{ -			return true; // abort -		} -	} - -	if(mImagePriority < F_ALMOST_ZERO) -	{ -		if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR) -		{ -			return true; // abort -		} -	} -	if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP) -	{ -		//nowhere to get data, abort. -		return true ; -	} - -	if (mFetcher->mDebugPause) -	{ -		return false; // debug: don't do any work -	} -	if (mID == mFetcher->mDebugID) -	{ -		mFetcher->mDebugCount++; // for setting breakpoints -	} - -	if (mState != DONE) -	{ -		mFetchTimer.reset(); -	} - -	if (mState == INIT) -	{		 -		mRawImage = NULL ; -		mRequestedDiscard = -1; -		mLoadedDiscard = -1; -		mDecodedDiscard = -1; -		mRequestedSize = 0; -		mFileSize = 0; -		mCachedSize = 0; -		mLoaded = FALSE; -		mSentRequest = UNSENT; -		mDecoded  = FALSE; -		mWritten  = FALSE; -		delete[] mBuffer; -		mBuffer = NULL; -		mBufferSize = 0; -		mHaveAllData = FALSE; -		clearPackets(); // TODO: Shouldn't be necessary -		mCacheReadHandle = LLTextureCache::nullHandle(); -		mCacheWriteHandle = LLTextureCache::nullHandle(); -		mState = LOAD_FROM_TEXTURE_CACHE; -		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE -		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) -							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; -		// fall through -	} - -	if (mState == LOAD_FROM_TEXTURE_CACHE) -	{ -		if (mCacheReadHandle == LLTextureCache::nullHandle()) -		{ -			U32 cache_priority = mWorkPriority; -			S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; -			S32 size = mDesiredSize - offset; -			if (size <= 0) -			{ -				mState = CACHE_POST; -				return false; -			} -			mFileSize = 0; -			mLoaded = FALSE;			 -			 -			if (mUrl.compare(0, 7, "file://") == 0) -			{ -				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - -				// read file from local disk -				std::string filename = mUrl.substr(7, std::string::npos); -				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); -				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, -																		  offset, size, responder); -			} -			else if (mUrl.empty()) -			{ -				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it - -				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); -				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, -																		  offset, size, responder); -			} -			else if(mCanUseHTTP) -			{ -				if (!(mUrl.compare(0, 7, "http://") == 0)) -				{ -					// *TODO:?remove this warning -					llwarns << "Unknown URL Type: " << mUrl << llendl; -				} -				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -				mState = SEND_HTTP_REQ; -			} -			else -			{ -				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -				mState = LOAD_FROM_NETWORK; -			} -		} - -		if (mLoaded) -		{ -			// Make sure request is complete. *TODO: make this auto-complete -			if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false)) -			{ -				mCacheReadHandle = LLTextureCache::nullHandle(); -				mState = CACHE_POST; -				// fall through -			} -			else -			{ -				return false; -			} -		} -		else -		{ -			return false; -		} -	} - -	if (mState == CACHE_POST) -	{ -		mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; -		// Successfully loaded -		if ((mCachedSize >= mDesiredSize) || mHaveAllData) -		{ -			// we have enough data, decode it -			llassert_always(mFormattedImage->getDataSize() > 0); -			mLoadedDiscard = mDesiredDiscard; -			mState = DECODE_IMAGE; -			mWriteToCacheState = NOT_WRITE ; -			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() -								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) -								 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; -			// fall through -		} -		else -		{ -			if (mUrl.compare(0, 7, "file://") == 0) -			{ -				// failed to load local file, we're done. -				return true; -			} -			// need more data -			else -			{ -				LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; -				mState = LOAD_FROM_NETWORK; -			} -			// fall through -		} -	} - -	if (mState == LOAD_FROM_NETWORK) -	{ -		static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP"); - -// 		if (mHost != LLHost::invalid) get_url = false; -		if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. -		{ -			LLViewerRegion* region = NULL; -			if (mHost == LLHost::invalid) -				region = gAgent.getRegion(); -			else -				region = LLWorld::getInstance()->getRegion(mHost); - -			if (region) -			{ -				std::string http_url = region->getHttpUrl() ; -				if (!http_url.empty()) -				{ -					mUrl = http_url + "/?texture_id=" + mID.asString().c_str(); -					mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. -				} -				else -				{ -					mCanUseHTTP = false ; -				} -			} -			else -			{ -				// This will happen if not logged in or if a region deoes not have HTTP Texture enabled -				//llwarns << "Region not found for host: " << mHost << llendl; -				mCanUseHTTP = false; -			} -		} -		if (mCanUseHTTP && !mUrl.empty()) -		{ -			mState = LLTextureFetchWorker::SEND_HTTP_REQ; -			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -			if(mWriteToCacheState != NOT_WRITE) -			{ -				mWriteToCacheState = CAN_WRITE ; -			} -			// don't return, fall through to next state -		} -		else if (mSentRequest == UNSENT && mCanUseNET) -		{ -			// Add this to the network queue and sit here. -			// LLTextureFetch::update() will send off a request which will change our state -			mWriteToCacheState = CAN_WRITE ; -			mRequestedSize = mDesiredSize; -			mRequestedDiscard = mDesiredDiscard; -			mSentRequest = QUEUED; -			mFetcher->addToNetworkQueue(this); -			if (! mMetricsStartTime) -			{ -				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); -			} -			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, -														 false, -														 LLImageBase::TYPE_AVATAR_BAKE == mType); -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			 -			return false; -		} -		else -		{ -			// Shouldn't need to do anything here -			//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); -			// Make certain this is in the network queue -			//mFetcher->addToNetworkQueue(this); -			//if (! mMetricsStartTime) -			//{ -			//   mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); -			//} -			//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, -			//                                             LLImageBase::TYPE_AVATAR_BAKE == mType); -			//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return false; -		} -	} -	 -	if (mState == LOAD_FROM_SIMULATOR) -	{ -		if (mFormattedImage.isNull()) -		{ -			mFormattedImage = new LLImageJ2C; -		} -		if (processSimulatorPackets()) -		{ -			LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL; -			mFetcher->removeFromNetworkQueue(this, false); -			if (mFormattedImage.isNull() || !mFormattedImage->getDataSize()) -			{ -				// processSimulatorPackets() failed -// 				llwarns << "processSimulatorPackets() failed to load buffer" << llendl; -				return true; // failed -			} -			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -			mState = DECODE_IMAGE; -			mWriteToCacheState = SHOULD_WRITE; - -			if (mMetricsStartTime) -			{ -				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, -															  false, -															  LLImageBase::TYPE_AVATAR_BAKE == mType, -															  LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); -				mMetricsStartTime = 0; -			} -			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, -														 false, -														 LLImageBase::TYPE_AVATAR_BAKE == mType); -		} -		else -		{ -			mFetcher->addToNetworkQueue(this); // failsafe -			if (! mMetricsStartTime) -			{ -				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); -			} -			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, -														 false, -														 LLImageBase::TYPE_AVATAR_BAKE == mType); -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -		} -		return false; -	} -	 -	if (mState == SEND_HTTP_REQ) -	{ -		if(mCanUseHTTP) -		{ -			//NOTE: -			//control the number of the http requests issued for: -			//1, not openning too many file descriptors at the same time; -			//2, control the traffic of http so udp gets bandwidth. -			// -			static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 32 ; -			if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE) -			{ -				return false ; //wait. -			} - -			mFetcher->removeFromNetworkQueue(this, false); -			 -			S32 cur_size = 0; -			if (mFormattedImage.notNull()) -			{ -				cur_size = mFormattedImage->getDataSize(); // amount of data we already have -				if (mFormattedImage->getDiscardLevel() == 0) -				{ -					if(cur_size > 0) -					{ -						// We already have all the data, just decode it -						mLoadedDiscard = mFormattedImage->getDiscardLevel(); -						mState = DECODE_IMAGE; -						return false; -					} -					else -					{ -						return true ; //abort. -					} -				} -			} -			mRequestedSize = mDesiredSize; -			mRequestedDiscard = mDesiredDiscard; -			mRequestedSize -= cur_size; -			S32 offset = cur_size; -			mBufferSize = cur_size; // This will get modified by callbackHttpGet() -			 -			bool res = false; -			if (!mUrl.empty()) -			{ -				mLoaded = FALSE; -				mGetStatus = 0; -				mGetReason.clear(); -				LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset -									 << " Bytes: " << mRequestedSize -									 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth -									 << LL_ENDL; -				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -				mState = WAIT_HTTP_REQ;	 - -				mFetcher->addToHTTPQueue(mID); -				if (! mMetricsStartTime) -				{ -					mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); -				} -				LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, -															 true, -															 LLImageBase::TYPE_AVATAR_BAKE == mType); - -				// Will call callbackHttpGet when curl request completes -				std::vector<std::string> headers; -				headers.push_back("Accept: image/x-j2c"); -				res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, -															  new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true)); -			} -			if (!res) -			{ -				llwarns << "HTTP GET request failed for " << mID << llendl; -				resetFormattedData(); -				++mHTTPFailCount; -				return true; // failed -			} -			// fall through -		} -		else //can not use http fetch. -		{ -			return true ; //abort -		} -	} -	 -	if (mState == WAIT_HTTP_REQ) -	{ -		if (mLoaded) -		{ -			S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; -			if (mRequestedSize < 0) -			{ -				S32 max_attempts; -				if (mGetStatus == HTTP_NOT_FOUND) -				{ -					mHTTPFailCount = max_attempts = 1; // Don't retry -					llwarns << "Texture missing from server (404): " << mUrl << llendl; - -					//roll back to try UDP -					if(mCanUseNET) -					{ -						mState = INIT ; -						mCanUseHTTP = false ; -						setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -						return false ; -					} -				} -				else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) -				{ -					// *TODO: Should probably introduce a timer here to delay future HTTP requsts -					// for a short time (~1s) to ease server load? Ideally the server would queue -					// requests instead of returning 503... we already limit the number pending. -					++mHTTPFailCount; -					max_attempts = mHTTPFailCount+1; // Keep retrying -					LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; -				} -				else -				{ -					const S32 HTTP_MAX_RETRY_COUNT = 3; -					max_attempts = HTTP_MAX_RETRY_COUNT + 1; -					++mHTTPFailCount; -					llinfos << "HTTP GET failed for: " << mUrl -							<< " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" -							<< " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; -				} - -				if (mHTTPFailCount >= max_attempts) -				{ -					if (cur_size > 0) -					{ -						// Use available data -						mLoadedDiscard = mFormattedImage->getDiscardLevel(); -						mState = DECODE_IMAGE; -						return false;  -					} -					else -					{ -						resetFormattedData(); -						mState = DONE; -						return true; // failed -					} -				} -				else -				{ -					mState = SEND_HTTP_REQ; -					return false; // retry -				} -			} -			 -			llassert_always(mBufferSize == cur_size + mRequestedSize); -			if(!mBufferSize)//no data received. -			{ -				delete[] mBuffer;  -				mBuffer = NULL; - -				//abort. -				mState = DONE; -				return true; -			} - -			if (mFormattedImage.isNull()) -			{ -				// For now, create formatted image based on extension -				std::string extension = gDirUtilp->getExtension(mUrl); -				mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); -				if (mFormattedImage.isNull()) -				{ -					mFormattedImage = new LLImageJ2C; // default -				} -			} -						 -			if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. -			{ -				mFileSize = mBufferSize; -			} -			else //the file size is unknown. -			{ -				mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. -			} -			 -			U8* buffer = new U8[mBufferSize]; -			if (cur_size > 0) -			{ -				memcpy(buffer, mFormattedImage->getData(), cur_size); -			} -			memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append -			// NOTE: setData releases current data and owns new data (buffer) -			mFormattedImage->setData(buffer, mBufferSize); -			// delete temp data -			delete[] mBuffer; // Note: not 'buffer' (assigned in setData()) -			mBuffer = NULL; -			mBufferSize = 0; -			mLoadedDiscard = mRequestedDiscard; -			mState = DECODE_IMAGE; -			if(mWriteToCacheState != NOT_WRITE) -			{ -				mWriteToCacheState = SHOULD_WRITE ; -			} -			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -			return false; -		} -		else -		{ -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return false; -		} -	} -	 -	if (mState == DECODE_IMAGE) -	{ -		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); -		if(textures_decode_disabled) -		{ -			// for debug use, don't decode -			mState = DONE; -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return true; -		} - -		if (mDesiredDiscard < 0) -		{ -			// We aborted, don't decode -			mState = DONE; -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return true; -		} -		 -		if (mFormattedImage->getDataSize() <= 0) -		{ -			//llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl; -			 -			//abort, don't decode -			mState = DONE; -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return true; -		} -		if (mLoadedDiscard < 0) -		{ -			//llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl; - -			//abort, don't decode -			mState = DONE; -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return true; -		} -		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it -		mRawImage = NULL; -		mAuxImage = NULL; -		llassert_always(mFormattedImage.notNull()); -		S32 discard = mHaveAllData ? 0 : mLoadedDiscard; -		U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; -		mDecoded  = FALSE; -		mState = DECODE_IMAGE_UPDATE; -		LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard -				<< " All Data: " << mHaveAllData << LL_ENDL; -		mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux, -																  new DecodeResponder(mFetcher, mID, this)); -		// fall though -	} -	 -	if (mState == DECODE_IMAGE_UPDATE) -	{ -		if (mDecoded) -		{ -			if (mDecodedDiscard < 0) -			{ -				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL; -				if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0) -				{ -					// Cache file should be deleted, try again -// 					llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl; -					llassert_always(mDecodeHandle == 0); -					mFormattedImage = NULL; -					++mRetryAttempt; -					setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -					mState = INIT; -					return false; -				} -				else -				{ -// 					llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; -					mState = DONE; // failed -				} -			} -			else -			{ -				llassert_always(mRawImage.notNull()); -				LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard -						<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; -				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -				mState = WRITE_TO_CACHE; -			} -			// fall through -		} -		else -		{ -			return false; -		} -	} - -	if (mState == WRITE_TO_CACHE) -	{ -		if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) -		{ -			// If we're in a local cache or we didn't actually receive any new data, -			// or we failed to load anything, skip -			mState = DONE; -			return false; -		} -		S32 datasize = mFormattedImage->getDataSize(); -		if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed. -		{ -			if(mHaveAllData) -			{ -				mFileSize = datasize ; -			} -			else -			{ -				mFileSize = datasize + 1 ; //flag not fully loaded. -			} -		} -		llassert_always(datasize); -		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it -		U32 cache_priority = mWorkPriority; -		mWritten = FALSE; -		mState = WAIT_ON_WRITE; -		CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); -		mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, -																  mFormattedImage->getData(), datasize, -																  mFileSize, responder); -		// fall through -	} -	 -	if (mState == WAIT_ON_WRITE) -	{ -		if (writeToCacheComplete()) -		{ -			mState = DONE; -			// fall through -		} -		else -		{ -			if (mDesiredDiscard < mDecodedDiscard) -			{ -				// We're waiting for this write to complete before we can receive more data -				// (we can't touch mFormattedImage until the write completes) -				// Prioritize the write -				mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle); -			} -			return false; -		} -	} - -	if (mState == DONE) -	{ -		if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) -		{ -			// More data was requested, return to INIT -			mState = INIT; -			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -			return false; -		} -		else -		{ -			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); -			return true; -		} -	} -	 -	return false; -} - -// Called from MAIN thread -void LLTextureFetchWorker::endWork(S32 param, bool aborted) -{ -	if (mDecodeHandle != 0) -	{ -		mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false); -		mDecodeHandle = 0; -	} -	mFormattedImage = NULL; -} - -////////////////////////////////////////////////////////////////////////////// - -// virtual -void LLTextureFetchWorker::finishWork(S32 param, bool completed) -{ -	// The following are required in case the work was aborted -	if (mCacheReadHandle != LLTextureCache::nullHandle()) -	{ -		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); -		mCacheReadHandle = LLTextureCache::nullHandle(); -	} -	if (mCacheWriteHandle != LLTextureCache::nullHandle()) -	{ -		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); -		mCacheWriteHandle = LLTextureCache::nullHandle(); -	} -} - -// virtual -bool LLTextureFetchWorker::deleteOK() -{ -	bool delete_ok = true; -	// Allow any pending reads or writes to complete -	if (mCacheReadHandle != LLTextureCache::nullHandle()) -	{ -		if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) -		{ -			mCacheReadHandle = LLTextureCache::nullHandle(); -		} -		else -		{ -			delete_ok = false; -		} -	} -	if (mCacheWriteHandle != LLTextureCache::nullHandle()) -	{ -		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) -		{ -			mCacheWriteHandle = LLTextureCache::nullHandle(); -		} -		else -		{ -			delete_ok = false; -		} -	} - -	if ((haveWork() && -		 // not ok to delete from these states -		 ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) -	{ -		delete_ok = false; -	} -	 -	return delete_ok; -} - -void LLTextureFetchWorker::removeFromCache() -{ -	if (!mInLocalCache) -	{ -		mFetcher->mTextureCache->removeFromCache(mID); -	} -} - - -////////////////////////////////////////////////////////////////////////////// - -bool LLTextureFetchWorker::processSimulatorPackets() -{ -	if (mFormattedImage.isNull() || mRequestedSize < 0) -	{ -		// not sure how we got here, but not a valid state, abort! -		llassert_always(mDecodeHandle == 0); -		mFormattedImage = NULL; -		return true; -	} -	 -	if (mLastPacket >= mFirstPacket) -	{ -		S32 buffer_size = mFormattedImage->getDataSize(); -		for (S32 i = mFirstPacket; i<=mLastPacket; i++) -		{ -			llassert_always(mPackets[i]); -			buffer_size += mPackets[i]->mSize; -		} -		bool have_all_data = mLastPacket >= mTotalPackets-1; -		if (mRequestedSize <= 0) -		{ -			// We received a packed but haven't requested anything yet (edge case) -			// Return true (we're "done") since we didn't request anything -			return true; -		} -		if (buffer_size >= mRequestedSize || have_all_data) -		{ -			/// We have enough (or all) data -			if (have_all_data) -			{ -				mHaveAllData = TRUE; -			} -			S32 cur_size = mFormattedImage->getDataSize(); -			if (buffer_size > cur_size) -			{ -				/// We have new data -				U8* buffer = new U8[buffer_size]; -				S32 offset = 0; -				if (cur_size > 0 && mFirstPacket > 0) -				{ -					memcpy(buffer, mFormattedImage->getData(), cur_size); -					offset = cur_size; -				} -				for (S32 i=mFirstPacket; i<=mLastPacket; i++) -				{ -					memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize); -					offset += mPackets[i]->mSize; -				} -				// NOTE: setData releases current data -				mFormattedImage->setData(buffer, buffer_size); -			} -			mLoadedDiscard = mRequestedDiscard; -			return true; -		} -	} -	return false; -} - -////////////////////////////////////////////////////////////////////////////// - -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, -										   const LLIOPipe::buffer_ptr_t& buffer, -										   bool partial, bool success) -{ -	S32 data_size = 0 ; - -	LLMutexLock lock(&mWorkMutex); - -	if (mState != WAIT_HTTP_REQ) -	{ -		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID -				<< " req=" << mSentRequest << " state= " << mState << llendl; -		return data_size; -	} -	if (mLoaded) -	{ -		llwarns << "Duplicate callback for " << mID.asString() << llendl; -		return data_size ; // ignore duplicate callback -	} -	if (success) -	{ -		// get length of stream: -		data_size = buffer->countAfter(channels.in(), NULL);		 -	 -		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; -		if (data_size > 0) -		{ -			// *TODO: set the formatted image data here directly to avoid the copy -			mBuffer = new U8[data_size]; -			buffer->readAfter(channels.in(), NULL, mBuffer, data_size); -			mBufferSize += data_size; -			if (data_size < mRequestedSize && mRequestedDiscard == 0) -			{ -				mHaveAllData = TRUE; -			} -			else if (data_size > mRequestedSize) -			{ -				// *TODO: This shouldn't be happening any more -				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; -				mHaveAllData = TRUE; -				llassert_always(mDecodeHandle == 0); -				mFormattedImage = NULL; // discard any previous data we had -				mBufferSize = data_size; -			} -		} -		else -		{ -			// We requested data but received none (and no error), -			// so presumably we have all of it -			mHaveAllData = TRUE; -		} -		mRequestedSize = data_size; -	} -	else -	{ -		mRequestedSize = -1; // error -	} -	mLoaded = TRUE; -	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - -	return data_size ; -} - -////////////////////////////////////////////////////////////////////////////// - -void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, -											 S32 imagesize, BOOL islocal) -{ -	LLMutexLock lock(&mWorkMutex); -	if (mState != LOAD_FROM_TEXTURE_CACHE) -	{ -// 		llwarns << "Read callback for " << mID << " with state = " << mState << llendl; -		return; -	} -	if (success) -	{ -		llassert_always(imagesize >= 0); -		mFileSize = imagesize; -		mFormattedImage = image; -		mImageCodec = image->getCodec(); -		mInLocalCache = islocal; -		if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize) -		{ -			mHaveAllData = TRUE; -		} -	} -	mLoaded = TRUE; -	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} - -void LLTextureFetchWorker::callbackCacheWrite(bool success) -{ -	LLMutexLock lock(&mWorkMutex); -	if (mState != WAIT_ON_WRITE) -	{ -// 		llwarns << "Write callback for " << mID << " with state = " << mState << llendl; -		return; -	} -	mWritten = TRUE; -	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} - -////////////////////////////////////////////////////////////////////////////// - -void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) -{ -	LLMutexLock lock(&mWorkMutex); -	if (mDecodeHandle == 0) -	{ -		return; // aborted, ignore -	} -	if (mState != DECODE_IMAGE_UPDATE) -	{ -// 		llwarns << "Decode callback for " << mID << " with state = " << mState << llendl; -		mDecodeHandle = 0; -		return; -	} -	llassert_always(mFormattedImage.notNull()); -	 -	mDecodeHandle = 0; -	if (success) -	{ -		llassert_always(raw); -		mRawImage = raw; -		mAuxImage = aux; -		mDecodedDiscard = mFormattedImage->getDiscardLevel(); - 		LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard -							 << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; -	} -	else -	{ -		llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl; -		removeFromCache(); -		mDecodedDiscard = -1; // Redundant, here for clarity and paranoia -	} -	mDecoded = TRUE; -// 	llinfos << mID << " : DECODE COMPLETE " << llendl; -	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} - -////////////////////////////////////////////////////////////////////////////// - -bool LLTextureFetchWorker::writeToCacheComplete() -{ -	// Complete write to cache -	if (mCacheWriteHandle != LLTextureCache::nullHandle()) -	{ -		if (!mWritten) -		{ -			return false; -		} -		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) -		{ -			mCacheWriteHandle = LLTextureCache::nullHandle(); -		} -		else -		{ -			return false; -		} -	} -	return true; -} - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// public - -LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode) -	: LLWorkerThread("TextureFetch", threaded), -	  mDebugCount(0), -	  mDebugPause(FALSE), -	  mPacketCount(0), -	  mBadPacketCount(0), -	  mQueueMutex(getAPRPool()), -	  mNetworkQueueMutex(getAPRPool()), -	  mTextureCache(cache), -	  mImageDecodeThread(imagedecodethread), -	  mTextureBandwidth(0), -	  mHTTPTextureBits(0), -	  mCurlGetRequest(NULL), -	  mQAMode(qa_mode) -{ -	mCurlPOSTRequestCount = 0; -	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); -	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); -} - -LLTextureFetch::~LLTextureFetch() -{ -	clearDeleteList() ; - -	while (! mCommands.empty()) -	{ -		TFRequest * req(mCommands.front()); -		mCommands.erase(mCommands.begin()); -		delete req; -	} -	 -	// ~LLQueuedThread() called here -} - -bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, -								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http) -{ -	if (mDebugPause) -	{ -		return false; -	} -	 -	LLTextureFetchWorker* worker = getWorker(id) ; -	if (worker) -	{ -		if (worker->mHost != host) -		{ -			llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: " -					<< host << " != " << worker->mHost << llendl; -			removeRequest(worker, true); -			worker = NULL; -			return false; -		} -	} - -	S32 desired_size; -	std::string exten = gDirUtilp->getExtension(url); -	if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) -	{ -		// Only do partial requests for J2C at the moment -		desired_size = MAX_IMAGE_DATA_SIZE; -		desired_discard = 0; -	} -	else if (desired_discard == 0) -	{ -		// if we want the entire image, and we know its size, then get it all -		// (calcDataSizeJ2C() below makes assumptions about how the image -		// was compressed - this code ensures that when we request the entire image, -		// we really do get it.) -		desired_size = MAX_IMAGE_DATA_SIZE; -	} -	else if (w*h*c > 0) -	{ -		// If the requester knows the dimensions of the image, -		// this will calculate how much data we need without having to parse the header - -		desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard); -	} -	else -	{ -		desired_size = TEXTURE_CACHE_ENTRY_SIZE; -		desired_discard = MAX_DISCARD_LEVEL; -	} - -	 -	if (worker) -	{ -		if (worker->wasAborted()) -		{ -			return false; // need to wait for previous aborted request to complete -		} -		worker->lockWorkMutex(); -		worker->mActiveCount++; -		worker->mNeedsAux = needs_aux; -		worker->setImagePriority(priority); -		worker->setDesiredDiscard(desired_discard, desired_size); -		worker->setCanUseHTTP(can_use_http) ; -		if (!worker->haveWork()) -		{ -			worker->mState = LLTextureFetchWorker::INIT; -			worker->unlockWorkMutex(); - -			worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); -		} -		else -		{ -			worker->unlockWorkMutex(); -		} -	} -	else -	{ -		worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); -		lockQueue() ; -		mRequestMap[id] = worker; -		unlockQueue() ; - -		worker->lockWorkMutex(); -		worker->mActiveCount++; -		worker->mNeedsAux = needs_aux; -		worker->setCanUseHTTP(can_use_http) ; -		worker->unlockWorkMutex(); -	} -	 -// 	llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; -	return true; -} - -// protected -void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) -{ -	lockQueue() ; -	bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; -	unlockQueue() ; - -	LLMutexLock lock(&mNetworkQueueMutex); -	if (in_request_map) -	{ -		// only add to the queue if in the request map -		// i.e. a delete has not been requested -		mNetworkQueue.insert(worker->mID); -	} -	for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); -		 iter1 != mCancelQueue.end(); ++iter1) -	{ -		iter1->second.erase(worker->mID); -	} -} - -void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) -{ -	LLMutexLock lock(&mNetworkQueueMutex); -	size_t erased = mNetworkQueue.erase(worker->mID); -	if (cancel && erased > 0) -	{ -		mCancelQueue[worker->mHost].insert(worker->mID); -	} -} - -// protected -void LLTextureFetch::addToHTTPQueue(const LLUUID& id) -{ -	LLMutexLock lock(&mNetworkQueueMutex); -	mHTTPTextureQueue.insert(id); -} - -void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) -{ -	LLMutexLock lock(&mNetworkQueueMutex); -	mHTTPTextureQueue.erase(id); -	mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits	 -} - -void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) -{ -	lockQueue() ; -	LLTextureFetchWorker* worker = getWorkerAfterLock(id); -	if (worker) -	{		 -		size_t erased_1 = mRequestMap.erase(worker->mID); -		unlockQueue() ; - -		llassert_always(erased_1 > 0) ; - -		removeFromNetworkQueue(worker, cancel); -		llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - -		worker->scheduleDelete();	 -	} -	else -	{ -		unlockQueue() ; -	} -} - -void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) -{ -	lockQueue() ; -	size_t erased_1 = mRequestMap.erase(worker->mID); -	unlockQueue() ; - -	llassert_always(erased_1 > 0) ; -	removeFromNetworkQueue(worker, cancel); -	llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - -	worker->scheduleDelete();	 -} - -S32 LLTextureFetch::getNumRequests()  -{  -	lockQueue() ; -	S32 size = (S32)mRequestMap.size();  -	unlockQueue() ; - -	return size ; -} - -S32 LLTextureFetch::getNumHTTPRequests()  -{  -	mNetworkQueueMutex.lock() ; -	S32 size = (S32)mHTTPTextureQueue.size();  -	mNetworkQueueMutex.unlock() ; - -	return size ; -} - -// call lockQueue() first! -LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) -{ -	LLTextureFetchWorker* res = NULL; -	map_t::iterator iter = mRequestMap.find(id); -	if (iter != mRequestMap.end()) -	{ -		res = iter->second; -	} -	return res; -} - -LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) -{ -	LLMutexLock lock(&mQueueMutex) ; - -	return getWorkerAfterLock(id) ; -} - - -bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, -										LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux) -{ -	bool res = false; -	LLTextureFetchWorker* worker = getWorker(id); -	if (worker) -	{ -		if (worker->wasAborted()) -		{ -			res = true; -		} -		else if (!worker->haveWork()) -		{ -			// Should only happen if we set mDebugPause... -			if (!mDebugPause) -			{ -// 				llwarns << "Adding work for inactive worker: " << id << llendl; -				worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); -			} -		} -		else if (worker->checkWork()) -		{ -			worker->lockWorkMutex(); -			discard_level = worker->mDecodedDiscard; -			raw = worker->mRawImage; -			aux = worker->mAuxImage; -			res = true; -			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; -			worker->unlockWorkMutex(); -		} -		else -		{ -			worker->lockWorkMutex(); -			if ((worker->mDecodedDiscard >= 0) && -				(worker->mDecodedDiscard < discard_level || discard_level < 0) && -				(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) -			{ -				// Not finished, but data is ready -				discard_level = worker->mDecodedDiscard; -				raw = worker->mRawImage; -				aux = worker->mAuxImage; -			} -			worker->unlockWorkMutex(); -		} -	} -	else -	{ -		res = true; -	} -	return res; -} - -bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) -{ -	bool res = false; -	LLTextureFetchWorker* worker = getWorker(id); -	if (worker) -	{ -		worker->lockWorkMutex(); -		worker->setImagePriority(priority); -		worker->unlockWorkMutex(); -		res = true; -	} -	return res; -} - -// Replicates and expands upon the base class's -// getPending() implementation.  getPending() and -// runCondition() replicate one another's logic to -// an extent and are sometimes used for the same -// function (deciding whether or not to sleep/pause -// a thread).  So the implementations need to stay -// in step, at least until this can be refactored and -// the redundancy eliminated. -// -// May be called from any thread - -//virtual -S32 LLTextureFetch::getPending() -{ -	S32 res; -	lockData(); -    { -        LLMutexLock lock(&mQueueMutex); -         -        res = mRequestQueue.size(); -        res += mCurlPOSTRequestCount; -        res += mCommands.size(); -    } -	unlockData(); -	return res; -} - -// virtual -bool LLTextureFetch::runCondition() -{ -	// Caller is holding the lock on LLThread's condition variable. -	 -	// LLQueuedThread, unlike its base class LLThread, makes this a -	// private method which is unfortunate.  I want to use it directly -	// but I'm going to have to re-implement the logic here (or change -	// declarations, which I don't want to do right now). -	// -	// Changes here may need to be reflected in getPending(). -	 -	bool have_no_commands(false); -	{ -		LLMutexLock lock(&mQueueMutex); -		 -		have_no_commands = mCommands.empty(); -	} -	 -    bool have_no_curl_requests(0 == mCurlPOSTRequestCount); -	 -	return ! (have_no_commands -			  && have_no_curl_requests -			  && (mRequestQueue.empty() && mIdleThread));		// From base class -} - -////////////////////////////////////////////////////////////////////////////// - -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) -void LLTextureFetch::commonUpdate() -{ -	// Run a cross-thread command, if any. -	cmdDoWork(); -	 -	// Update Curl on same thread as mCurlGetRequest was constructed -	S32 processed = mCurlGetRequest->process(); -	if (processed > 0) -	{ -		lldebugs << "processed: " << processed << " messages." << llendl; -	} -} - - -// MAIN THREAD -//virtual -S32 LLTextureFetch::update(U32 max_time_ms) -{ -	static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS"); - -	{ -		mNetworkQueueMutex.lock() ; -		mMaxBandwidth = band_width ; - -		gTextureList.sTextureBits += mHTTPTextureBits ; -		mHTTPTextureBits = 0 ; - -		mNetworkQueueMutex.unlock() ; -	} - -	S32 res = LLWorkerThread::update(max_time_ms); -	 -	if (!mDebugPause) -	{ -		sendRequestListToSimulators(); -	} - -	if (!mThreaded) -	{ -		commonUpdate(); -	} - -	return res; -} - -//called in the MAIN thread after the TextureCacheThread shuts down. -void LLTextureFetch::shutDownTextureCacheThread()  -{ -	if(mTextureCache) -	{ -		llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ; -		mTextureCache = NULL ; -	} -} -	 -//called in the MAIN thread after the ImageDecodeThread shuts down. -void LLTextureFetch::shutDownImageDecodeThread()  -{ -	if(mImageDecodeThread) -	{ -		llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ; -		mImageDecodeThread = NULL ; -	} -} - -// WORKER THREAD -void LLTextureFetch::startThread() -{ -	// Construct mCurlGetRequest from Worker Thread -	mCurlGetRequest = new LLCurlRequest(); -} - -// WORKER THREAD -void LLTextureFetch::endThread() -{ -	// Destroy mCurlGetRequest from Worker Thread -	delete mCurlGetRequest; -	mCurlGetRequest = NULL; -} - -// WORKER THREAD -void LLTextureFetch::threadedUpdate() -{ -	llassert_always(mCurlGetRequest); -	 -	// Limit update frequency -	const F32 PROCESS_TIME = 0.05f;  -	static LLFrameTimer process_timer; -	if (process_timer.getElapsedTimeF32() < PROCESS_TIME) -	{ -		return; -	} -	process_timer.reset(); -	 -	commonUpdate(); - -#if 0 -	const F32 INFO_TIME = 1.0f;  -	static LLFrameTimer info_timer; -	if (info_timer.getElapsedTimeF32() >= INFO_TIME) -	{ -		S32 q = mCurlGetRequest->getQueued(); -		if (q > 0) -		{ -			llinfos << "Queued gets: " << q << llendl; -			info_timer.reset(); -		} -	} -#endif -	 -} - -////////////////////////////////////////////////////////////////////////////// - -void LLTextureFetch::sendRequestListToSimulators() -{ -	// All requests -	const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps -	 -	// Sim requests -	const S32 IMAGES_PER_REQUEST = 50; -	const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp -	const F32 MIN_REQUEST_TIME = 1.0f; -	const F32 MIN_DELTA_PRIORITY = 1000.f; - -	// Periodically, gather the list of textures that need data from the network -	// And send the requests out to the simulators -	static LLFrameTimer timer; -	if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME) -	{ -		return; -	} -	timer.reset(); -	 -	// Send requests -	typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t; -	typedef std::map< LLHost, request_list_t > work_request_map_t; -	work_request_map_t requests; -	{ -	LLMutexLock lock2(&mNetworkQueueMutex); -	for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) -	{ -		queue_t::iterator curiter = iter++; -		LLTextureFetchWorker* req = getWorker(*curiter); -		if (!req) -		{ -			mNetworkQueue.erase(curiter); -			continue; // paranoia -		} -		if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && -			(req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) -		{ -			// We already received our URL, remove from the queue -			llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; -			mNetworkQueue.erase(curiter); -			continue; -		} -		if (req->mID == mDebugID) -		{ -			mDebugCount++; // for setting breakpoints -		} -		if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && -			req->mTotalPackets > 0 && -			req->mLastPacket >= req->mTotalPackets-1) -		{ -			// We have all the packets... make sure this is high priority -// 			req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); -			continue; -		} -		F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); -		{ -			F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); -			if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || -				(delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || -				(elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) -			{ -				requests[req->mHost].insert(req); -			} -		} -	} -	} - -	for (work_request_map_t::iterator iter1 = requests.begin(); -		 iter1 != requests.end(); ++iter1) -	{ -		LLHost host = iter1->first; -		// invalid host = use agent host -		if (host == LLHost::invalid) -		{ -			host = gAgent.getRegionHost(); -		} - -		S32 sim_request_count = 0; -		 -		for (request_list_t::iterator iter2 = iter1->second.begin(); -			 iter2 != iter1->second.end(); ++iter2) -		{ -			LLTextureFetchWorker* req = *iter2; -			if (gMessageSystem) -			{ -				if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) -				{ -					// Initialize packet data based on data read from cache -					req->lockWorkMutex(); -					req->setupPacketData(); -					req->unlockWorkMutex(); -				} -				if (0 == sim_request_count) -				{ -					gMessageSystem->newMessageFast(_PREHASH_RequestImage); -					gMessageSystem->nextBlockFast(_PREHASH_AgentData); -					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -				} -				S32 packet = req->mLastPacket + 1; -				gMessageSystem->nextBlockFast(_PREHASH_RequestImage); -				gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID); -				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard); -				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority); -				gMessageSystem->addU32Fast(_PREHASH_Packet, packet); -				gMessageSystem->addU8Fast(_PREHASH_Type, req->mType); -// 				llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard -// 						<< " Packet: " << packet << " Priority: " << req->mImagePriority << llendl; - -				static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); -				static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); -				if (log_to_viewer_log || log_to_sim) -				{ -					mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime()); -					mTextureInfo.setRequestOffset(req->mID, 0); -					mTextureInfo.setRequestSize(req->mID, 0); -					mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); -				} - -				req->lockWorkMutex(); -				req->mSentRequest = LLTextureFetchWorker::SENT_SIM; -				req->mSimRequestedDiscard = req->mDesiredDiscard; -				req->mRequestedPriority = req->mImagePriority; -				req->mRequestedTimer.reset(); -				req->unlockWorkMutex(); -				sim_request_count++; -				if (sim_request_count >= IMAGES_PER_REQUEST) -				{ -// 					llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; - -					gMessageSystem->sendSemiReliable(host, NULL, NULL); -					sim_request_count = 0; -				} -			} -		} -		if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST) -		{ -// 			llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; -			gMessageSystem->sendSemiReliable(host, NULL, NULL); -			sim_request_count = 0; -		} -	} -	 -	// Send cancelations -	{ -	LLMutexLock lock2(&mNetworkQueueMutex); -	if (gMessageSystem && !mCancelQueue.empty()) -	{ -		for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); -			 iter1 != mCancelQueue.end(); ++iter1) -		{ -			LLHost host = iter1->first; -			if (host == LLHost::invalid) -			{ -				host = gAgent.getRegionHost(); -			} -			S32 request_count = 0; -			for (queue_t::iterator iter2 = iter1->second.begin(); -				 iter2 != iter1->second.end(); ++iter2) -			{ -				if (0 == request_count) -				{ -					gMessageSystem->newMessageFast(_PREHASH_RequestImage); -					gMessageSystem->nextBlockFast(_PREHASH_AgentData); -					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); -				} -				gMessageSystem->nextBlockFast(_PREHASH_RequestImage); -				gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); -				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); -				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); -				gMessageSystem->addU32Fast(_PREHASH_Packet, 0); -				gMessageSystem->addU8Fast(_PREHASH_Type, 0); -// 				llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - -				request_count++; -				if (request_count >= IMAGES_PER_REQUEST) -				{ -					gMessageSystem->sendSemiReliable(host, NULL, NULL); -					request_count = 0; -				} -			} -			if (request_count > 0 && request_count < IMAGES_PER_REQUEST) -			{ -				gMessageSystem->sendSemiReliable(host, NULL, NULL); -			} -		} -		mCancelQueue.clear(); -	} -	} -} - -////////////////////////////////////////////////////////////////////////////// - -bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) -{ -	mRequestedTimer.reset(); -	if (index >= mTotalPackets) -	{ -// 		llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl; -		return false; -	} -	if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE) -	{ -// 		llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl; -		return false; -	} -	 -	if (index >= (S32)mPackets.size()) -	{ -		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers -	} -	else if (mPackets[index] != NULL) -	{ -// 		llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl; -		return false; -	} - -	mPackets[index] = new PacketData(data, size); -	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL) -	{ -		++mLastPacket; -	} -	return true; -} - -bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, -										U16 data_size, U8* data) -{ -	LLTextureFetchWorker* worker = getWorker(id); -	bool res = true; - -	++mPacketCount; -	 -	if (!worker) -	{ -// 		llwarns << "Received header for non active worker: " << id << llendl; -		res = false; -	} -	else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK || -			 worker->mSentRequest != LLTextureFetchWorker::SENT_SIM) -	{ -// 		llwarns << "receiveImageHeader for worker: " << id -// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] -// 				<< " sent: " << worker->mSentRequest << llendl; -		res = false; -	} -	else if (worker->mLastPacket != -1) -	{ -		// check to see if we've gotten this packet before -// 		llwarns << "Received duplicate header for: " << id << llendl; -		res = false; -	} -	else if (!data_size) -	{ -// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl; -		res = false; -	} -	if (!res) -	{ -		++mBadPacketCount; -		mNetworkQueueMutex.lock() ; -		mCancelQueue[host].insert(id); -		mNetworkQueueMutex.unlock() ; -		return false; -	} - -	worker->lockWorkMutex(); - -	//	Copy header data into image object -	worker->mImageCodec = codec; -	worker->mTotalPackets = packets; -	worker->mFileSize = (S32)totalbytes;	 -	llassert_always(totalbytes > 0); -	llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); -	res = worker->insertPacket(0, data, data_size); -	worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); -	worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; -	worker->unlockWorkMutex(); -	return res; -} - -bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) -{ -	LLTextureFetchWorker* worker = getWorker(id); -	bool res = true; - -	++mPacketCount; -	 -	if (!worker) -	{ -// 		llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl; -		res = false; -	} -	else if (worker->mLastPacket == -1) -	{ -// 		llwarns << "Received packet " << packet_num << " before header for: " << id << llendl; -		res = false; -	} -	else if (!data_size) -	{ -// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl; -		res = false; -	} -	if (!res) -	{ -		++mBadPacketCount; -		mNetworkQueueMutex.lock() ; -		mCancelQueue[host].insert(id); -		mNetworkQueueMutex.unlock() ; -		return false; -	} - -	worker->lockWorkMutex(); -	 -	res = worker->insertPacket(packet_num, data, data_size); -	 -	if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || -		(worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) -	{ -		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); -		worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; -	} -	else -	{ -// 		llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id -// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; -		removeFromNetworkQueue(worker, true); // failsafe -	} - -	if(packet_num >= (worker->mTotalPackets - 1)) -	{ -		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); -		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - -		if (log_to_viewer_log || log_to_sim) -		{ -			U64 timeNow = LLTimer::getTotalTime(); -			mTextureInfo.setRequestSize(id, worker->mFileSize); -			mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); -		} -	} -	worker->unlockWorkMutex(); - -	return res; -} - -////////////////////////////////////////////////////////////////////////////// -BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) -{ -	BOOL from_cache = FALSE ; - -	LLTextureFetchWorker* worker = getWorker(id); -	if (worker) -	{ -		worker->lockWorkMutex() ; -		from_cache = worker->mInLocalCache ; -		worker->unlockWorkMutex() ; -	} - -	return from_cache ; -} - -S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, -								  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) -{ -	S32 state = LLTextureFetchWorker::INVALID; -	F32 data_progress = 0.0f; -	F32 requested_priority = 0.0f; -	F32 fetch_dtime = 999999.f; -	F32 request_dtime = 999999.f; -	U32 fetch_priority = 0; -	 -	LLTextureFetchWorker* worker = getWorker(id); -	if (worker && worker->haveWork()) -	{ -		worker->lockWorkMutex(); -		state = worker->mState; -		fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); -		request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); -		if (worker->mFileSize > 0) -		{ -			if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) -			{ -				S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE; -				data_size = llmax(data_size, 0); -				data_progress = (F32)data_size / (F32)worker->mFileSize; -			} -			else if (worker->mFormattedImage.notNull()) -			{ -				data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; -			} -		} -		if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ) -		{ -			requested_priority = worker->mRequestedPriority; -		} -		else -		{ -			requested_priority = worker->mImagePriority; -		} -		fetch_priority = worker->getPriority(); -		can_use_http = worker->getCanUseHTTP() ; -		worker->unlockWorkMutex(); -	} -	data_progress_p = data_progress; -	requested_priority_p = requested_priority; -	fetch_priority_p = fetch_priority; -	fetch_dtime_p = fetch_dtime; -	request_dtime_p = request_dtime; -	return state; -} - -void LLTextureFetch::dump() -{ -	llinfos << "LLTextureFetch REQUESTS:" << llendl; -	for (request_queue_t::iterator iter = mRequestQueue.begin(); -		 iter != mRequestQueue.end(); ++iter) -	{ -		LLQueuedThread::QueuedRequest* qreq = *iter; -		LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq; -		LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass(); -		llinfos << " ID: " << worker->mID -				<< " PRI: " << llformat("0x%08x",wreq->getPriority()) -				<< " STATE: " << worker->sStateDescs[worker->mState] -				<< llendl; -	} -} - -////////////////////////////////////////////////////////////////////////////// - -// cross-thread command methods - -void LLTextureFetch::commandSetRegion(U64 region_handle) -{ -	TFReqSetRegion * req = new TFReqSetRegion(region_handle); - -	cmdEnqueue(req); -} - -void LLTextureFetch::commandSendMetrics(const std::string & caps_url, -										const LLUUID & session_id, -										const LLUUID & agent_id, -										LLViewerAssetStats * main_stats) -{ -	TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats); - -	cmdEnqueue(req); -} - -void LLTextureFetch::commandDataBreak() -{ -	// The pedantically correct way to implement this is to create a command -	// request object in the above fashion and enqueue it.  However, this is -	// simple data of an advisorial not operational nature and this case -	// of shared-write access is tolerable. - -	LLTextureFetch::svMetricsDataBreak = true; -} - -void LLTextureFetch::cmdEnqueue(TFRequest * req) -{ -	lockQueue(); -	mCommands.push_back(req); -	unlockQueue(); - -	unpause(); -} - -LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() -{ -	TFRequest * ret = 0; -	 -	lockQueue(); -	if (! mCommands.empty()) -	{ -		ret = mCommands.front(); -		mCommands.erase(mCommands.begin()); -	} -	unlockQueue(); - -	return ret; -} - -void LLTextureFetch::cmdDoWork() -{ -	if (mDebugPause) -	{ -		return;  // debug: don't do any work -	} - -	TFRequest * req = cmdDequeue(); -	if (req) -	{ -		// One request per pass should really be enough for this. -		req->doWork(this); -		delete req; -	} -} - - -////////////////////////////////////////////////////////////////////////////// - -// Private (anonymous) class methods implementing the command scheme. - -namespace -{ - -/** - * Implements the 'Set Region' command. - * - * Thread:  Thread1 (TextureFetch) - */ -bool -TFReqSetRegion::doWork(LLTextureFetch *) -{ -	LLViewerAssetStatsFF::set_region_thread1(mRegionHandle); - -	return true; -} - - -TFReqSendMetrics::~TFReqSendMetrics() -{ -	delete mMainStats; -	mMainStats = 0; -} - - -/** - * Implements the 'Send Metrics' command.  Takes over - * ownership of the passed LLViewerAssetStats pointer. - * - * Thread:  Thread1 (TextureFetch) - */ -bool -TFReqSendMetrics::doWork(LLTextureFetch * fetcher) -{ -	/* -	 * HTTP POST responder.  Doesn't do much but tries to -	 * detect simple breaks in recording the metrics stream. -	 * -	 * The 'volatile' modifiers don't indicate signals, -	 * mmap'd memory or threads, really.  They indicate that -	 * the referenced data is part of a pseudo-closure for -	 * this responder rather than being required for correct -	 * operation. -     * -     * We don't try very hard with the POST request.  We give -     * it one shot and that's more-or-less it.  With a proper -     * refactoring of the LLQueuedThread usage, these POSTs -     * could be put in a request object and made more reliable. -	 */ -	class lcl_responder : public LLCurl::Responder -	{ -	public: -		lcl_responder(LLTextureFetch * fetcher, -					  S32 expected_sequence, -                      volatile const S32 & live_sequence, -                      volatile bool & reporting_break, -					  volatile bool & reporting_started) -			: LLCurl::Responder(), -			  mFetcher(fetcher), -              mExpectedSequence(expected_sequence), -              mLiveSequence(live_sequence), -			  mReportingBreak(reporting_break), -			  mReportingStarted(reporting_started) -			{ -                mFetcher->incrCurlPOSTCount(); -            } -         -        ~lcl_responder() -            { -                mFetcher->decrCurlPOSTCount(); -            } - -		// virtual -		void error(U32 status_num, const std::string & reason) -			{ -                if (mLiveSequence == mExpectedSequence) -                { -                    mReportingBreak = true; -                } -				LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service.  Reason:  " -									<< reason << LL_ENDL; -			} - -		// virtual -		void result(const LLSD & content) -			{ -                if (mLiveSequence == mExpectedSequence) -                { -                    mReportingBreak = false; -                    mReportingStarted = true; -                } -			} - -	private: -		LLTextureFetch * mFetcher; -        S32 mExpectedSequence; -        volatile const S32 & mLiveSequence; -		volatile bool & mReportingBreak; -		volatile bool & mReportingStarted; - -	}; // class lcl_responder -	 -	if (! gViewerAssetStatsThread1) -		return true; - -	static volatile bool reporting_started(false); -	static volatile S32 report_sequence(0); -     -	// We've taken over ownership of the stats copy at this -	// point.  Get a working reference to it for merging here -	// but leave it in 'this'.  Destructor will rid us of it. -	LLViewerAssetStats & main_stats = *mMainStats; - -	// Merge existing stats into those from main, convert to LLSD -	main_stats.merge(*gViewerAssetStatsThread1); -	LLSD merged_llsd = main_stats.asLLSD(true); - -	// Add some additional meta fields to the content -	merged_llsd["session_id"] = mSessionID; -	merged_llsd["agent_id"] = mAgentID; -	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics -	merged_llsd["sequence"] = report_sequence;						// Sequence number -	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer -	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report -		 -	// Update sequence number -	if (S32_MAX == ++report_sequence) -		report_sequence = 0; - -	// Limit the size of the stats report if necessary. -	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); - -	if (! mCapsURL.empty()) -	{ -		LLCurlRequest::headers_t headers; -		fetcher->getCurlRequest().post(mCapsURL, -									   headers, -									   merged_llsd, -									   new lcl_responder(fetcher, -														 report_sequence, -                                                         report_sequence, -                                                         LLTextureFetch::svMetricsDataBreak, -														 reporting_started)); -	} -	else -	{ -		LLTextureFetch::svMetricsDataBreak = true; -	} - -	// In QA mode, Metrics submode, log the result for ease of testing -	if (fetcher->isQAMode()) -	{ -		LL_INFOS("Textures") << merged_llsd << LL_ENDL; -	} - -	gViewerAssetStatsThread1->reset(); - -	return true; -} - - -bool -truncate_viewer_metrics(int max_regions, LLSD & metrics) -{ -	static const LLSD::String reg_tag("regions"); -	static const LLSD::String duration_tag("duration"); -	 -	LLSD & reg_map(metrics[reg_tag]); -	if (reg_map.size() <= max_regions) -	{ -		return false; -	} - -	// Build map of region hashes ordered by duration -	typedef std::multimap<LLSD::Real, int> reg_ordered_list_t; -	reg_ordered_list_t regions_by_duration; - -	int ind(0); -	LLSD::array_const_iterator it_end(reg_map.endArray()); -	for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind) -	{ -		LLSD::Real duration = (*it)[duration_tag].asReal(); -		regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind)); -	} - -	// Build a replacement regions array with the longest-persistence regions -	LLSD new_region(LLSD::emptyArray()); -	reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend()); -	reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin()); -	for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2) -	{ -		new_region.append(reg_map[it2->second]); -	} -	reg_map = new_region; -	 -	return true; -} - -} // end of anonymous namespace - - - +/** 
 + * @file lltexturefetch.cpp
 + * @brief Object which fetches textures from the cache and/or network
 + *
 + * $LicenseInfo:firstyear=2000&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, Inc.
 + * 
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation;
 + * version 2.1 of the License only.
 + * 
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + * 
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 + * 
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +#include "llviewerprecompiledheaders.h"
 +
 +#include <iostream>
 +#include <map>
 +
 +#include "llstl.h"
 +
 +#include "lltexturefetch.h"
 +
 +#include "llcurl.h"
 +#include "lldir.h"
 +#include "llhttpclient.h"
 +#include "llhttpstatuscodes.h"
 +#include "llimage.h"
 +#include "llimagej2c.h"
 +#include "llimageworker.h"
 +#include "llworkerthread.h"
 +#include "message.h"
 +
 +#include "llagent.h"
 +#include "lltexturecache.h"
 +#include "llviewercontrol.h"
 +#include "llviewertexturelist.h"
 +#include "llviewertexture.h"
 +#include "llviewerregion.h"
 +#include "llviewerstats.h"
 +#include "llviewerassetstats.h"
 +#include "llworld.h"
 +
 +//////////////////////////////////////////////////////////////////////////////
 +class LLTextureFetchWorker : public LLWorkerClass
 +{
 +	friend class LLTextureFetch;
 +	friend class HTTPGetResponder;
 +	
 +private:
 +	class CacheReadResponder : public LLTextureCache::ReadResponder
 +	{
 +	public:
 +		CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image)
 +			: mFetcher(fetcher), mID(id)
 +		{
 +			setImage(image);
 +		}
 +		virtual void completed(bool success)
 +		{
 +			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 +			if (worker)
 +			{
 + 				worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal);
 +			}
 +		}
 +	private:
 +		LLTextureFetch* mFetcher;
 +		LLUUID mID;
 +	};
 +
 +	class CacheWriteResponder : public LLTextureCache::WriteResponder
 +	{
 +	public:
 +		CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id)
 +			: mFetcher(fetcher), mID(id)
 +		{
 +		}
 +		virtual void completed(bool success)
 +		{
 +			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 +			if (worker)
 +			{
 +				worker->callbackCacheWrite(success);
 +			}
 +		}
 +	private:
 +		LLTextureFetch* mFetcher;
 +		LLUUID mID;
 +	};
 +	
 +	class DecodeResponder : public LLImageDecodeThread::Responder
 +	{
 +	public:
 +		DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker)
 +			: mFetcher(fetcher), mID(id), mWorker(worker)
 +		{
 +		}
 +		virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux)
 +		{
 +			LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 +			if (worker)
 +			{
 + 				worker->callbackDecoded(success, raw, aux);
 +			}
 +		}
 +	private:
 +		LLTextureFetch* mFetcher;
 +		LLUUID mID;
 +		LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID)
 +	};
 +
 +	struct Compare
 +	{
 +		// lhs < rhs
 +		bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const
 +		{
 +			// greater priority is "less"
 +			const F32 lpriority = lhs->mImagePriority;
 +			const F32 rpriority = rhs->mImagePriority;
 +			if (lpriority > rpriority) // higher priority
 +				return true;
 +			else if (lpriority < rpriority)
 +				return false;
 +			else
 +				return lhs < rhs;
 +		}
 +	};
 +	
 +public:
 +	/*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
 +	/*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
 +	/*virtual*/ bool deleteOK(); // called from update() (WORK THREAD)
 +
 +	~LLTextureFetchWorker();
 +	// void relese() { --mActiveCount; }
 +
 +	S32 callbackHttpGet(const LLChannelDescriptors& channels,
 +						 const LLIOPipe::buffer_ptr_t& buffer,
 +						 bool partial, bool success);
 +	void callbackCacheRead(bool success, LLImageFormatted* image,
 +						   S32 imagesize, BOOL islocal);
 +	void callbackCacheWrite(bool success);
 +	void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux);
 +	
 +	void setGetStatus(U32 status, const std::string& reason)
 +	{
 +		LLMutexLock lock(&mWorkMutex);
 +
 +		mGetStatus = status;
 +		mGetReason = reason;
 +	}
 +
 +	void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; }
 +	bool getCanUseHTTP() const { return mCanUseHTTP; }
 +
 +	LLTextureFetch & getFetcher() { return *mFetcher; }
 +	
 +protected:
 +	LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host,
 +						 F32 priority, S32 discard, S32 size);
 +
 +private:
 +	/*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD)
 +	/*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
 +
 +	void resetFormattedData();
 +	
 +	void setImagePriority(F32 priority);
 +	void setDesiredDiscard(S32 discard, S32 size);
 +	bool insertPacket(S32 index, U8* data, S32 size);
 +	void clearPackets();
 +	void setupPacketData();
 +	U32 calcWorkPriority();
 +	void removeFromCache();
 +	bool processSimulatorPackets();
 +	bool writeToCacheComplete();
 +	
 +	void lockWorkMutex() { mWorkMutex.lock(); }
 +	void unlockWorkMutex() { mWorkMutex.unlock(); }
 +
 +private:
 +	enum e_state // mState
 +	{
 +		// NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack)
 +		INVALID = 0,
 +		INIT,
 +		LOAD_FROM_TEXTURE_CACHE,
 +		CACHE_POST,
 +		LOAD_FROM_NETWORK,
 +		LOAD_FROM_SIMULATOR,
 +		SEND_HTTP_REQ,
 +		WAIT_HTTP_REQ,
 +		DECODE_IMAGE,
 +		DECODE_IMAGE_UPDATE,
 +		WRITE_TO_CACHE,
 +		WAIT_ON_WRITE,
 +		DONE
 +	};
 +	enum e_request_state // mSentRequest
 +	{
 +		UNSENT = 0,
 +		QUEUED = 1,
 +		SENT_SIM = 2
 +	};
 +	enum e_write_to_cache_state //mWriteToCacheState
 +	{
 +		NOT_WRITE = 0,
 +		CAN_WRITE = 1,
 +		SHOULD_WRITE = 2
 +	};
 +	static const char* sStateDescs[];
 +	e_state mState;
 +	e_write_to_cache_state mWriteToCacheState;
 +	LLTextureFetch* mFetcher;
 +	LLPointer<LLImageFormatted> mFormattedImage;
 +	LLPointer<LLImageRaw> mRawImage;
 +	LLPointer<LLImageRaw> mAuxImage;
 +	LLUUID mID;
 +	LLHost mHost;
 +	std::string mUrl;
 +	U8 mType;
 +	F32 mImagePriority;
 +	U32 mWorkPriority;
 +	F32 mRequestedPriority;
 +	S32 mDesiredDiscard;
 +	S32 mSimRequestedDiscard;
 +	S32 mRequestedDiscard;
 +	S32 mLoadedDiscard;
 +	S32 mDecodedDiscard;
 +	LLFrameTimer mRequestedTimer;
 +	LLFrameTimer mFetchTimer;
 +	LLTextureCache::handle_t mCacheReadHandle;
 +	LLTextureCache::handle_t mCacheWriteHandle;
 +	U8* mBuffer;
 +	S32 mBufferSize;
 +	S32 mRequestedSize;
 +	S32 mDesiredSize;
 +	S32 mFileSize;
 +	S32 mCachedSize;	
 +	e_request_state mSentRequest;
 +	handle_t mDecodeHandle;
 +	BOOL mLoaded;
 +	BOOL mDecoded;
 +	BOOL mWritten;
 +	BOOL mNeedsAux;
 +	BOOL mHaveAllData;
 +	BOOL mInLocalCache;
 +	bool mCanUseHTTP ;
 +	bool mCanUseNET ; //can get from asset server.
 +	S32 mHTTPFailCount;
 +	S32 mRetryAttempt;
 +	S32 mActiveCount;
 +	U32 mGetStatus;
 +	std::string mGetReason;
 +	
 +	// Work Data
 +	LLMutex mWorkMutex;
 +	struct PacketData
 +	{
 +		PacketData(U8* data, S32 size) { mData = data; mSize = size; }
 +		~PacketData() { clearData(); }
 +		void clearData() { delete[] mData; mData = NULL; }
 +		U8* mData;
 +		U32 mSize;
 +	};
 +	std::vector<PacketData*> mPackets;
 +	S32 mFirstPacket;
 +	S32 mLastPacket;
 +	U16 mTotalPackets;
 +	U8 mImageCodec;
 +
 +	LLViewerAssetStats::duration_t mMetricsStartTime;
 +};
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +class HTTPGetResponder : public LLCurl::Responder
 +{
 +	LOG_CLASS(HTTPGetResponder);
 +public:
 +	HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir)
 +		: mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir)
 +	{
 +	}
 +	~HTTPGetResponder()
 +	{
 +	}
 +
 +	virtual void completedRaw(U32 status, const std::string& reason,
 +							  const LLChannelDescriptors& channels,
 +							  const LLIOPipe::buffer_ptr_t& buffer)
 +	{
 +		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
 +		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
 +		static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ;
 +
 +		if (log_to_viewer_log || log_to_sim)
 +		{
 +			mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime);
 +			U64 timeNow = LLTimer::getTotalTime();
 +			mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP);
 +			mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize);
 +			mFetcher->mTextureInfo.setRequestOffset(mID, mOffset);
 +			mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow);
 +		}
 +
 +		lldebugs << "HTTP COMPLETE: " << mID << llendl;
 +		LLTextureFetchWorker* worker = mFetcher->getWorker(mID);
 +		if (worker)
 +		{
 +			bool success = false;
 +			bool partial = false;
 +			if (HTTP_OK <= status &&  status < HTTP_MULTIPLE_CHOICES)
 +			{
 +				success = true;
 +				if (HTTP_PARTIAL_CONTENT == status) // partial information
 +				{
 +					partial = true;
 +				}
 +			}
 +
 +			if (!success)
 +			{
 +				worker->setGetStatus(status, reason);
 +// 				llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl;
 +			}
 +			
 +			S32 data_size = worker->callbackHttpGet(channels, buffer, partial, success);
 +			
 +			if(log_texture_traffic && data_size > 0)
 +			{
 +				LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ;
 +				if(tex)
 +				{
 +					gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ;
 +				}
 +			}
 +
 +			mFetcher->removeFromHTTPQueue(mID, data_size);
 +
 +			if (worker->mMetricsStartTime)
 +			{
 +				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
 +															  true,
 +															  LLImageBase::TYPE_AVATAR_BAKE == worker->mType,
 +															  LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime);
 +				worker->mMetricsStartTime = 0;
 +			}
 +			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
 +														 true,
 +														 LLImageBase::TYPE_AVATAR_BAKE == worker->mType);
 +		}
 +		else
 +		{
 +			mFetcher->removeFromHTTPQueue(mID);
 + 			llwarns << "Worker not found: " << mID << llendl;
 +		}
 +	}
 +
 +	virtual bool followRedir()
 +	{
 +		return mFollowRedir;
 +	}
 +	
 +private:
 +	LLTextureFetch* mFetcher;
 +	LLUUID mID;
 +	U64 mStartTime;
 +	S32 mRequestedSize;
 +	U32 mOffset;
 +	bool mFollowRedir;
 +};
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +// Cross-thread messaging for asset metrics.
 +
 +/**
 + * @brief Base class for cross-thread requests made of the fetcher
 + *
 + * I believe the intent of the LLQueuedThread class was to
 + * have these operations derived from LLQueuedThread::QueuedRequest
 + * but the texture fetcher has elected to manage the queue
 + * in its own manner.  So these are free-standing objects which are
 + * managed in simple FIFO order on the mCommands queue of the
 + * LLTextureFetch object.
 + *
 + * What each represents is a simple command sent from an
 + * outside thread into the TextureFetch thread to be processed
 + * in order and in a timely fashion (though not an absolute
 + * higher priority than other operations of the thread).
 + * Each operation derives a new class from the base customizing
 + * members, constructors and the doWork() method to effect
 + * the command.
 + *
 + * The flow is one-directional.  There are two global instances
 + * of the LLViewerAssetStats collector, one for the main program's
 + * thread pointed to by gViewerAssetStatsMain and one for the
 + * TextureFetch thread pointed to by gViewerAssetStatsThread1.
 + * Common operations has each thread recording metrics events
 + * into the respective collector unconcerned with locking and
 + * the state of any other thread.  But when the agent moves into
 + * a different region or the metrics timer expires and a report
 + * needs to be sent back to the grid, messaging across threads
 + * is required to distribute data and perform global actions.
 + * In pseudo-UML, it looks like:
 + *
 + *                       Main                 Thread1
 + *                        .                      .
 + *                        .                      .
 + *                     +-----+                   .
 + *                     | AM  |                   .
 + *                     +--+--+                   .
 + *      +-------+         |                      .
 + *      | Main  |      +--+--+                   .
 + *      |       |      | SRE |---.               .
 + *      | Stats |      +-----+    \              .
 + *      |       |         |        \  (uuid)  +-----+
 + *      | Coll. |      +--+--+      `-------->| SR  |
 + *      +-------+      | MSC |                +--+--+
 + *         | ^         +-----+                   |
 + *         | |  (uuid)  / .                   +-----+ (uuid)
 + *         |  `--------'  .                   | MSC |---------.
 + *         |              .                   +-----+         |
 + *         |           +-----+                   .            v
 + *         |           | TE  |                   .        +-------+
 + *         |           +--+--+                   .        | Thd1  |
 + *         |              |                      .        |       |
 + *         |           +-----+                   .        | Stats |
 + *          `--------->| RSC |                   .        |       |
 + *                     +--+--+                   .        | Coll. |
 + *                        |                      .        +-------+
 + *                     +--+--+                   .            |
 + *                     | SME |---.               .            |
 + *                     +-----+    \              .            |
 + *                        .        \ (clone)  +-----+         |
 + *                        .         `-------->| SM  |         |
 + *                        .                   +--+--+         |
 + *                        .                      |            |
 + *                        .                   +-----+         |
 + *                        .                   | RSC |<--------'
 + *                        .                   +-----+
 + *                        .                      |
 + *                        .                   +-----+
 + *                        .                   | CP  |--> HTTP POST
 + *                        .                   +-----+
 + *                        .                      .
 + *                        .                      .
 + *
 + *
 + * Key:
 + *
 + * SRE - Set Region Enqueued.  Enqueue a 'Set Region' command in
 + *       the other thread providing the new UUID of the region.
 + *       TFReqSetRegion carries the data.
 + * SR  - Set Region.  New region UUID is sent to the thread-local
 + *       collector.
 + * SME - Send Metrics Enqueued.  Enqueue a 'Send Metrics' command
 + *       including an ownership transfer of a cloned LLViewerAssetStats.
 + *       TFReqSendMetrics carries the data.
 + * SM  - Send Metrics.  Global metrics reporting operation.  Takes
 + *       the cloned stats from the command, merges it with the
 + *       thread's local stats, converts to LLSD and sends it on
 + *       to the grid.
 + * AM  - Agent Moved.  Agent has completed some sort of move to a
 + *       new region.
 + * TE  - Timer Expired.  Metrics timer has expired (on the order
 + *       of 10 minutes).
 + * CP  - CURL Post
 + * MSC - Modify Stats Collector.  State change in the thread-local
 + *       collector.  Typically a region change which affects the
 + *       global pointers used to find the 'current stats'.
 + * RSC - Read Stats Collector.  Extract collector data cloning it
 + *       (i.e. deep copy) when necessary.
 + *
 + */
 +class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest
 +{
 +public:
 +	// Default ctors and assignment operator are correct.
 +
 +	virtual ~TFRequest()
 +		{}
 +
 +	// Patterned after QueuedRequest's method but expected behavior
 +	// is different.  Always expected to complete on the first call
 +	// and work dispatcher will assume the same and delete the
 +	// request after invocation.
 +	virtual bool doWork(LLTextureFetch * fetcher) = 0;
 +};
 +
 +namespace 
 +{
 +
 +/**
 + * @brief Implements a 'Set Region' cross-thread command.
 + *
 + * When an agent moves to a new region, subsequent metrics need
 + * to be binned into a new or existing stats collection in 1:1
 + * relationship with the region.  We communicate this region
 + * change across the threads involved in the communication with
 + * this message.
 + *
 + * Corresponds to LLTextureFetch::commandSetRegion()
 + */
 +class TFReqSetRegion : public LLTextureFetch::TFRequest
 +{
 +public:
 +	TFReqSetRegion(U64 region_handle)
 +		: LLTextureFetch::TFRequest(),
 +		  mRegionHandle(region_handle)
 +		{}
 +	TFReqSetRegion & operator=(const TFReqSetRegion &);	// Not defined
 +
 +	virtual ~TFReqSetRegion()
 +		{}
 +
 +	virtual bool doWork(LLTextureFetch * fetcher);
 +		
 +public:
 +	const U64 mRegionHandle;
 +};
 +
 +
 +/**
 + * @brief Implements a 'Send Metrics' cross-thread command.
 + *
 + * This is the big operation.  The main thread gathers metrics
 + * for a period of minutes into LLViewerAssetStats and other
 + * objects then makes a snapshot of the data by cloning the
 + * collector.  This command transfers the clone, along with a few
 + * additional arguments (UUIDs), handing ownership to the
 + * TextureFetch thread.  It then merges its own data into the
 + * cloned copy, converts to LLSD and kicks off an HTTP POST of
 + * the resulting data to the currently active metrics collector.
 + *
 + * Corresponds to LLTextureFetch::commandSendMetrics()
 + */
 +class TFReqSendMetrics : public LLTextureFetch::TFRequest
 +{
 +public:
 +    /**
 +	 * Construct the 'Send Metrics' command to have the TextureFetch
 +	 * thread add and log metrics data.
 +	 *
 +	 * @param	caps_url		URL of a "ViewerMetrics" Caps target
 +	 *							to receive the data.  Does not have to
 +	 *							be associated with a particular region.
 +	 *
 +	 * @param	session_id		UUID of the agent's session.
 +	 *
 +	 * @param	agent_id		UUID of the agent.  (Being pure here...)
 +	 *
 +	 * @param	main_stats		Pointer to a clone of the main thread's
 +	 *							LLViewerAssetStats data.  Thread1 takes
 +	 *							ownership of the copy and disposes of it
 +	 *							when done.
 +	 */
 +	TFReqSendMetrics(const std::string & caps_url,
 +					 const LLUUID & session_id,
 +					 const LLUUID & agent_id,
 +					 LLViewerAssetStats * main_stats)
 +		: LLTextureFetch::TFRequest(),
 +		  mCapsURL(caps_url),
 +		  mSessionID(session_id),
 +		  mAgentID(agent_id),
 +		  mMainStats(main_stats)
 +		{}
 +	TFReqSendMetrics & operator=(const TFReqSendMetrics &);	// Not defined
 +
 +	virtual ~TFReqSendMetrics();
 +
 +	virtual bool doWork(LLTextureFetch * fetcher);
 +		
 +public:
 +	const std::string mCapsURL;
 +	const LLUUID mSessionID;
 +	const LLUUID mAgentID;
 +	LLViewerAssetStats * mMainStats;
 +};
 +
 +/*
 + * Examines the merged viewer metrics report and if found to be too long,
 + * will attempt to truncate it in some reasonable fashion.
 + *
 + * @param		max_regions		Limit of regions allowed in report.
 + *
 + * @param		metrics			Full, merged viewer metrics report.
 + *
 + * @returns		If data was truncated, returns true.
 + */
 +bool truncate_viewer_metrics(int max_regions, LLSD & metrics);
 +
 +} // end of anonymous namespace
 +
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +//static
 +const char* LLTextureFetchWorker::sStateDescs[] = {
 +	"INVALID",
 +	"INIT",
 +	"LOAD_FROM_TEXTURE_CACHE",
 +	"CACHE_POST",
 +	"LOAD_FROM_NETWORK",
 +	"LOAD_FROM_SIMULATOR",
 +	"SEND_HTTP_REQ",
 +	"WAIT_HTTP_REQ",
 +	"DECODE_IMAGE",
 +	"DECODE_IMAGE_UPDATE",
 +	"WRITE_TO_CACHE",
 +	"WAIT_ON_WRITE",
 +	"DONE",
 +};
 +
 +// static
 +volatile bool LLTextureFetch::svMetricsDataBreak(true);	// Start with a data break
 +
 +// called from MAIN THREAD
 +
 +LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher,
 +										   const std::string& url, // Optional URL
 +										   const LLUUID& id,	// Image UUID
 +										   const LLHost& host,	// Simulator host
 +										   F32 priority,		// Priority
 +										   S32 discard,			// Desired discard
 +										   S32 size)			// Desired size
 +	: LLWorkerClass(fetcher, "TextureFetch"),
 +	  mState(INIT),
 +	  mWriteToCacheState(NOT_WRITE),
 +	  mFetcher(fetcher),
 +	  mID(id),
 +	  mHost(host),
 +	  mUrl(url),
 +	  mImagePriority(priority),
 +	  mWorkPriority(0),
 +	  mRequestedPriority(0.f),
 +	  mDesiredDiscard(-1),
 +	  mSimRequestedDiscard(-1),
 +	  mRequestedDiscard(-1),
 +	  mLoadedDiscard(-1),
 +	  mDecodedDiscard(-1),
 +	  mCacheReadHandle(LLTextureCache::nullHandle()),
 +	  mCacheWriteHandle(LLTextureCache::nullHandle()),
 +	  mBuffer(NULL),
 +	  mBufferSize(0),
 +	  mRequestedSize(0),
 +	  mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE),
 +	  mFileSize(0),
 +	  mCachedSize(0),
 +	  mLoaded(FALSE),
 +	  mSentRequest(UNSENT),
 +	  mDecodeHandle(0),
 +	  mDecoded(FALSE),
 +	  mWritten(FALSE),
 +	  mNeedsAux(FALSE),
 +	  mHaveAllData(FALSE),
 +	  mInLocalCache(FALSE),
 +	  mCanUseHTTP(true),
 +	  mHTTPFailCount(0),
 +	  mRetryAttempt(0),
 +	  mActiveCount(0),
 +	  mGetStatus(0),
 +	  mWorkMutex(NULL),
 +	  mFirstPacket(0),
 +	  mLastPacket(-1),
 +	  mTotalPackets(0),
 +	  mImageCodec(IMG_CODEC_INVALID),
 +	  mMetricsStartTime(0)
 +{
 +	mCanUseNET = mUrl.empty() ;
 +
 +	calcWorkPriority();
 +	mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL;
 +// 	llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl;
 +	if (!mFetcher->mDebugPause)
 +	{
 +		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
 +		addWork(0, work_priority );
 +	}
 +	setDesiredDiscard(discard, size);
 +}
 +
 +LLTextureFetchWorker::~LLTextureFetchWorker()
 +{
 +// 	llinfos << "Destroy: " << mID
 +// 			<< " Decoded=" << mDecodedDiscard
 +// 			<< " Requested=" << mRequestedDiscard
 +// 			<< " Desired=" << mDesiredDiscard << llendl;
 +	llassert_always(!haveWork());
 +	lockWorkMutex();
 +	if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
 +	{
 +		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
 +	}
 +	if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache)
 +	{
 +		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
 +	}
 +	mFormattedImage = NULL;
 +	clearPackets();
 +	unlockWorkMutex();
 +	mFetcher->removeFromHTTPQueue(mID);
 +}
 +
 +void LLTextureFetchWorker::clearPackets()
 +{
 +	for_each(mPackets.begin(), mPackets.end(), DeletePointer());
 +	mPackets.clear();
 +	mTotalPackets = 0;
 +	mLastPacket = -1;
 +	mFirstPacket = 0;
 +}
 +
 +void LLTextureFetchWorker::setupPacketData()
 +{
 +	S32 data_size = 0;
 +	if (mFormattedImage.notNull())
 +	{
 +		data_size = mFormattedImage->getDataSize();
 +	}
 +	if (data_size > 0)
 +	{
 +		// Only used for simulator requests
 +		mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1;
 +		if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size)
 +		{
 +			llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl;
 +			removeFromCache();
 +			resetFormattedData();
 +			clearPackets();
 +		}
 +		else if (mFileSize > 0)
 +		{
 +			mLastPacket = mFirstPacket-1;
 +			mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1;
 +		}
 +		else
 +		{
 +			// This file was cached using HTTP so we have to refetch the first packet
 +			resetFormattedData();
 +			clearPackets();
 +		}
 +	}
 +}
 +
 +U32 LLTextureFetchWorker::calcWorkPriority()
 +{
 + 	//llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority());
 +	static const F32 PRIORITY_SCALE = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerFetchedTexture::maxDecodePriority();
 +
 +	mWorkPriority = llmin((U32)LLWorkerThread::PRIORITY_LOWBITS, (U32)(mImagePriority * PRIORITY_SCALE));
 +	return mWorkPriority;
 +}
 +
 +// mWorkMutex is locked
 +void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size)
 +{
 +	bool prioritize = false;
 +	if (mDesiredDiscard != discard)
 +	{
 +		if (!haveWork())
 +		{
 +			calcWorkPriority();
 +			if (!mFetcher->mDebugPause)
 +			{
 +				U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
 +				addWork(0, work_priority);
 +			}
 +		}
 +		else if (mDesiredDiscard < discard)
 +		{
 +			prioritize = true;
 +		}
 +		mDesiredDiscard = discard;
 +		mDesiredSize = size;
 +	}
 +	else if (size > mDesiredSize)
 +	{
 +		mDesiredSize = size;
 +		prioritize = true;
 +	}
 +	mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE);
 +	if ((prioritize && mState == INIT) || mState == DONE)
 +	{
 +		mState = INIT;
 +		U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH;
 +		setPriority(work_priority);
 +	}
 +}
 +
 +void LLTextureFetchWorker::setImagePriority(F32 priority)
 +{
 +// 	llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority());
 +	F32 delta = fabs(priority - mImagePriority);
 +	if (delta > (mImagePriority * .05f) || mState == DONE)
 +	{
 +		mImagePriority = priority;
 +		calcWorkPriority();
 +		U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS);
 +		setPriority(work_priority);
 +	}
 +}
 +
 +void LLTextureFetchWorker::resetFormattedData()
 +{
 +	delete[] mBuffer;
 +	mBuffer = NULL;
 +	mBufferSize = 0;
 +	if (mFormattedImage.notNull())
 +	{
 +		mFormattedImage->deleteData();
 +	}
 +	mHaveAllData = FALSE;
 +}
 +
 +// Called from MAIN thread
 +void LLTextureFetchWorker::startWork(S32 param)
 +{
 +	llassert(mFormattedImage.isNull());
 +}
 +
 +#include "llviewertexturelist.h" // debug
 +
 +// Called from LLWorkerThread::processRequest()
 +bool LLTextureFetchWorker::doWork(S32 param)
 +{
 +	LLMutexLock lock(&mWorkMutex);
 +
 +	if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED)))
 +	{
 +		if (mState < DECODE_IMAGE)
 +		{
 +			return true; // abort
 +		}
 +	}
 +
 +	if(mImagePriority < F_ALMOST_ZERO)
 +	{
 +		if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR)
 +		{
 +			return true; // abort
 +		}
 +	}
 +	if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP)
 +	{
 +		//nowhere to get data, abort.
 +		return true ;
 +	}
 +
 +	if (mFetcher->mDebugPause)
 +	{
 +		return false; // debug: don't do any work
 +	}
 +	if (mID == mFetcher->mDebugID)
 +	{
 +		mFetcher->mDebugCount++; // for setting breakpoints
 +	}
 +
 +	if (mState != DONE)
 +	{
 +		mFetchTimer.reset();
 +	}
 +
 +	if (mState == INIT)
 +	{		
 +		mRawImage = NULL ;
 +		mRequestedDiscard = -1;
 +		mLoadedDiscard = -1;
 +		mDecodedDiscard = -1;
 +		mRequestedSize = 0;
 +		mFileSize = 0;
 +		mCachedSize = 0;
 +		mLoaded = FALSE;
 +		mSentRequest = UNSENT;
 +		mDecoded  = FALSE;
 +		mWritten  = FALSE;
 +		delete[] mBuffer;
 +		mBuffer = NULL;
 +		mBufferSize = 0;
 +		mHaveAllData = FALSE;
 +		clearPackets(); // TODO: Shouldn't be necessary
 +		mCacheReadHandle = LLTextureCache::nullHandle();
 +		mCacheWriteHandle = LLTextureCache::nullHandle();
 +		mState = LOAD_FROM_TEXTURE_CACHE;
 +		mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE
 +		LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority)
 +							 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
 +		// fall through
 +	}
 +
 +	if (mState == LOAD_FROM_TEXTURE_CACHE)
 +	{
 +		if (mCacheReadHandle == LLTextureCache::nullHandle())
 +		{
 +			U32 cache_priority = mWorkPriority;
 +			S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
 +			S32 size = mDesiredSize - offset;
 +			if (size <= 0)
 +			{
 +				mState = CACHE_POST;
 +				return false;
 +			}
 +			mFileSize = 0;
 +			mLoaded = FALSE;			
 +			
 +			if (mUrl.compare(0, 7, "file://") == 0)
 +			{
 +				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
 +
 +				// read file from local disk
 +				std::string filename = mUrl.substr(7, std::string::npos);
 +				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 +				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority,
 +																		  offset, size, responder);
 +			}
 +			else if (mUrl.empty())
 +			{
 +				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
 +
 +				CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage);
 +				mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority,
 +																		  offset, size, responder);
 +			}
 +			else if(mCanUseHTTP)
 +			{
 +				if (!(mUrl.compare(0, 7, "http://") == 0))
 +				{
 +					// *TODO:?remove this warning
 +					llwarns << "Unknown URL Type: " << mUrl << llendl;
 +				}
 +				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +				mState = SEND_HTTP_REQ;
 +			}
 +			else
 +			{
 +				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +				mState = LOAD_FROM_NETWORK;
 +			}
 +		}
 +
 +		if (mLoaded)
 +		{
 +			// Make sure request is complete. *TODO: make this auto-complete
 +			if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false))
 +			{
 +				mCacheReadHandle = LLTextureCache::nullHandle();
 +				mState = CACHE_POST;
 +				// fall through
 +			}
 +			else
 +			{
 +				return false;
 +			}
 +		}
 +		else
 +		{
 +			return false;
 +		}
 +	}
 +
 +	if (mState == CACHE_POST)
 +	{
 +		mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
 +		// Successfully loaded
 +		if ((mCachedSize >= mDesiredSize) || mHaveAllData)
 +		{
 +			// we have enough data, decode it
 +			llassert_always(mFormattedImage->getDataSize() > 0);
 +			mLoadedDiscard = mDesiredDiscard;
 +			mState = DECODE_IMAGE;
 +			mWriteToCacheState = NOT_WRITE ;
 +			LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize()
 +								 << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight())
 +								 << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL;
 +			// fall through
 +		}
 +		else
 +		{
 +			if (mUrl.compare(0, 7, "file://") == 0)
 +			{
 +				// failed to load local file, we're done.
 +				return true;
 +			}
 +			// need more data
 +			else
 +			{
 +				LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL;
 +				mState = LOAD_FROM_NETWORK;
 +			}
 +			// fall through
 +		}
 +	}
 +
 +	if (mState == LOAD_FROM_NETWORK)
 +	{
 +		static LLCachedControl<bool> use_http(gSavedSettings,"ImagePipelineUseHTTP");
 +
 +// 		if (mHost != LLHost::invalid) get_url = false;
 +		if ( use_http && mCanUseHTTP && mUrl.empty())//get http url.
 +		{
 +			LLViewerRegion* region = NULL;
 +			if (mHost == LLHost::invalid)
 +				region = gAgent.getRegion();
 +			else
 +				region = LLWorld::getInstance()->getRegion(mHost);
 +
 +			if (region)
 +			{
 +				std::string http_url = region->getHttpUrl() ;
 +				if (!http_url.empty())
 +				{
 +					mUrl = http_url + "/?texture_id=" + mID.asString().c_str();
 +					mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id.
 +				}
 +				else
 +				{
 +					mCanUseHTTP = false ;
 +				}
 +			}
 +			else
 +			{
 +				// This will happen if not logged in or if a region deoes not have HTTP Texture enabled
 +				//llwarns << "Region not found for host: " << mHost << llendl;
 +				mCanUseHTTP = false;
 +			}
 +		}
 +		if (mCanUseHTTP && !mUrl.empty())
 +		{
 +			mState = LLTextureFetchWorker::SEND_HTTP_REQ;
 +			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +			if(mWriteToCacheState != NOT_WRITE)
 +			{
 +				mWriteToCacheState = CAN_WRITE ;
 +			}
 +			// don't return, fall through to next state
 +		}
 +		else if (mSentRequest == UNSENT && mCanUseNET)
 +		{
 +			// Add this to the network queue and sit here.
 +			// LLTextureFetch::update() will send off a request which will change our state
 +			mWriteToCacheState = CAN_WRITE ;
 +			mRequestedSize = mDesiredSize;
 +			mRequestedDiscard = mDesiredDiscard;
 +			mSentRequest = QUEUED;
 +			mFetcher->addToNetworkQueue(this);
 +			if (! mMetricsStartTime)
 +			{
 +				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
 +			}
 +			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
 +														 false,
 +														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			
 +			return false;
 +		}
 +		else
 +		{
 +			// Shouldn't need to do anything here
 +			//llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end());
 +			// Make certain this is in the network queue
 +			//mFetcher->addToNetworkQueue(this);
 +			//if (! mMetricsStartTime)
 +			//{
 +			//   mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
 +			//}
 +			//LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false,
 +			//                                             LLImageBase::TYPE_AVATAR_BAKE == mType);
 +			//setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return false;
 +		}
 +	}
 +	
 +	if (mState == LOAD_FROM_SIMULATOR)
 +	{
 +		if (mFormattedImage.isNull())
 +		{
 +			mFormattedImage = new LLImageJ2C;
 +		}
 +		if (processSimulatorPackets())
 +		{
 +			LL_DEBUGS("Texture") << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL;
 +			mFetcher->removeFromNetworkQueue(this, false);
 +			if (mFormattedImage.isNull() || !mFormattedImage->getDataSize())
 +			{
 +				// processSimulatorPackets() failed
 +// 				llwarns << "processSimulatorPackets() failed to load buffer" << llendl;
 +				return true; // failed
 +			}
 +			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +			mState = DECODE_IMAGE;
 +			mWriteToCacheState = SHOULD_WRITE;
 +
 +			if (mMetricsStartTime)
 +			{
 +				LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE,
 +															  false,
 +															  LLImageBase::TYPE_AVATAR_BAKE == mType,
 +															  LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime);
 +				mMetricsStartTime = 0;
 +			}
 +			LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE,
 +														 false,
 +														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 +		}
 +		else
 +		{
 +			mFetcher->addToNetworkQueue(this); // failsafe
 +			if (! mMetricsStartTime)
 +			{
 +				mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
 +			}
 +			LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
 +														 false,
 +														 LLImageBase::TYPE_AVATAR_BAKE == mType);
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +		}
 +		return false;
 +	}
 +	
 +	if (mState == SEND_HTTP_REQ)
 +	{
 +		if(mCanUseHTTP)
 +		{
 +			//NOTE:
 +			//control the number of the http requests issued for:
 +			//1, not openning too many file descriptors at the same time;
 +			//2, control the traffic of http so udp gets bandwidth.
 +			//
 +			static const S32 MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE = 8 ;
 +			if(mFetcher->getNumHTTPRequests() > MAX_NUM_OF_HTTP_REQUESTS_IN_QUEUE)
 +			{
 +				return false ; //wait.
 +			}
 +
 +			mFetcher->removeFromNetworkQueue(this, false);
 +			
 +			S32 cur_size = 0;
 +			if (mFormattedImage.notNull())
 +			{
 +				cur_size = mFormattedImage->getDataSize(); // amount of data we already have
 +				if (mFormattedImage->getDiscardLevel() == 0)
 +				{
 +					if(cur_size > 0)
 +					{
 +						// We already have all the data, just decode it
 +						mLoadedDiscard = mFormattedImage->getDiscardLevel();
 +						mState = DECODE_IMAGE;
 +						return false;
 +					}
 +					else
 +					{
 +						return true ; //abort.
 +					}
 +				}
 +			}
 +			mRequestedSize = mDesiredSize;
 +			mRequestedDiscard = mDesiredDiscard;
 +			mRequestedSize -= cur_size;
 +			S32 offset = cur_size;
 +			mBufferSize = cur_size; // This will get modified by callbackHttpGet()
 +			
 +			bool res = false;
 +			if (!mUrl.empty())
 +			{
 +				mLoaded = FALSE;
 +				mGetStatus = 0;
 +				mGetReason.clear();
 +				LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset
 +									 << " Bytes: " << mRequestedSize
 +									 << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth
 +									 << LL_ENDL;
 +				setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +				mState = WAIT_HTTP_REQ;	
 +
 +				mFetcher->addToHTTPQueue(mID);
 +				if (! mMetricsStartTime)
 +				{
 +					mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp();
 +				}
 +				LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE,
 +															 true,
 +															 LLImageBase::TYPE_AVATAR_BAKE == mType);
 +
 +				// Will call callbackHttpGet when curl request completes
 +				std::vector<std::string> headers;
 +				headers.push_back("Accept: image/x-j2c");
 +				res = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize,
 +															  new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true));
 +			}
 +			if (!res)
 +			{
 +				llwarns << "HTTP GET request failed for " << mID << llendl;
 +				resetFormattedData();
 +				++mHTTPFailCount;
 +				return true; // failed
 +			}
 +			// fall through
 +		}
 +		else //can not use http fetch.
 +		{
 +			return true ; //abort
 +		}
 +	}
 +	
 +	if (mState == WAIT_HTTP_REQ)
 +	{
 +		if (mLoaded)
 +		{
 +			S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0;
 +			if (mRequestedSize < 0)
 +			{
 +				S32 max_attempts;
 +				if (mGetStatus == HTTP_NOT_FOUND)
 +				{
 +					mHTTPFailCount = max_attempts = 1; // Don't retry
 +					llwarns << "Texture missing from server (404): " << mUrl << llendl;
 +
 +					//roll back to try UDP
 +					if(mCanUseNET)
 +					{
 +						mState = INIT ;
 +						mCanUseHTTP = false ;
 +						setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +						return false ;
 +					}
 +				}
 +				else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE)
 +				{
 +					// *TODO: Should probably introduce a timer here to delay future HTTP requsts
 +					// for a short time (~1s) to ease server load? Ideally the server would queue
 +					// requests instead of returning 503... we already limit the number pending.
 +					++mHTTPFailCount;
 +					max_attempts = mHTTPFailCount+1; // Keep retrying
 +					LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL;
 +				}
 +				else
 +				{
 +					const S32 HTTP_MAX_RETRY_COUNT = 3;
 +					max_attempts = HTTP_MAX_RETRY_COUNT + 1;
 +					++mHTTPFailCount;
 +					llinfos << "HTTP GET failed for: " << mUrl
 +							<< " Status: " << mGetStatus << " Reason: '" << mGetReason << "'"
 +							<< " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl;
 +				}
 +
 +				if (mHTTPFailCount >= max_attempts)
 +				{
 +					if (cur_size > 0)
 +					{
 +						// Use available data
 +						mLoadedDiscard = mFormattedImage->getDiscardLevel();
 +						mState = DECODE_IMAGE;
 +						return false; 
 +					}
 +					else
 +					{
 +						resetFormattedData();
 +						mState = DONE;
 +						return true; // failed
 +					}
 +				}
 +				else
 +				{
 +					mState = SEND_HTTP_REQ;
 +					return false; // retry
 +				}
 +			}
 +			
 +			llassert_always(mBufferSize == cur_size + mRequestedSize);
 +			if(!mBufferSize)//no data received.
 +			{
 +				delete[] mBuffer; 
 +				mBuffer = NULL;
 +
 +				//abort.
 +				mState = DONE;
 +				return true;
 +			}
 +
 +			if (mFormattedImage.isNull())
 +			{
 +				// For now, create formatted image based on extension
 +				std::string extension = gDirUtilp->getExtension(mUrl);
 +				mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension));
 +				if (mFormattedImage.isNull())
 +				{
 +					mFormattedImage = new LLImageJ2C; // default
 +				}
 +			}
 +						
 +			if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded.
 +			{
 +				mFileSize = mBufferSize;
 +			}
 +			else //the file size is unknown.
 +			{
 +				mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded.
 +			}
 +			
 +			U8* buffer = new U8[mBufferSize];
 +			if (cur_size > 0)
 +			{
 +				memcpy(buffer, mFormattedImage->getData(), cur_size);
 +			}
 +			memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append
 +			// NOTE: setData releases current data and owns new data (buffer)
 +			mFormattedImage->setData(buffer, mBufferSize);
 +			// delete temp data
 +			delete[] mBuffer; // Note: not 'buffer' (assigned in setData())
 +			mBuffer = NULL;
 +			mBufferSize = 0;
 +			mLoadedDiscard = mRequestedDiscard;
 +			mState = DECODE_IMAGE;
 +			if(mWriteToCacheState != NOT_WRITE)
 +			{
 +				mWriteToCacheState = SHOULD_WRITE ;
 +			}
 +			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +			return false;
 +		}
 +		else
 +		{
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return false;
 +		}
 +	}
 +	
 +	if (mState == DECODE_IMAGE)
 +	{
 +		static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
 +		if(textures_decode_disabled)
 +		{
 +			// for debug use, don't decode
 +			mState = DONE;
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return true;
 +		}
 +
 +		if (mDesiredDiscard < 0)
 +		{
 +			// We aborted, don't decode
 +			mState = DONE;
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return true;
 +		}
 +		
 +		if (mFormattedImage->getDataSize() <= 0)
 +		{
 +			//llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl;
 +			
 +			//abort, don't decode
 +			mState = DONE;
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return true;
 +		}
 +		if (mLoadedDiscard < 0)
 +		{
 +			//llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl;
 +
 +			//abort, don't decode
 +			mState = DONE;
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return true;
 +		}
 +		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
 +		mRawImage = NULL;
 +		mAuxImage = NULL;
 +		llassert_always(mFormattedImage.notNull());
 +		S32 discard = mHaveAllData ? 0 : mLoadedDiscard;
 +		U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority;
 +		mDecoded  = FALSE;
 +		mState = DECODE_IMAGE_UPDATE;
 +		LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard
 +				<< " All Data: " << mHaveAllData << LL_ENDL;
 +		mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux,
 +																  new DecodeResponder(mFetcher, mID, this));
 +		// fall though
 +	}
 +	
 +	if (mState == DECODE_IMAGE_UPDATE)
 +	{
 +		if (mDecoded)
 +		{
 +			if (mDecodedDiscard < 0)
 +			{
 +				LL_DEBUGS("Texture") << mID << ": Failed to Decode." << LL_ENDL;
 +				if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0)
 +				{
 +					// Cache file should be deleted, try again
 +// 					llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl;
 +					llassert_always(mDecodeHandle == 0);
 +					mFormattedImage = NULL;
 +					++mRetryAttempt;
 +					setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +					mState = INIT;
 +					return false;
 +				}
 +				else
 +				{
 +// 					llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl;
 +					mState = DONE; // failed
 +				}
 +			}
 +			else
 +			{
 +				llassert_always(mRawImage.notNull());
 +				LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard
 +						<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
 +				setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +				mState = WRITE_TO_CACHE;
 +			}
 +			// fall through
 +		}
 +		else
 +		{
 +			return false;
 +		}
 +	}
 +
 +	if (mState == WRITE_TO_CACHE)
 +	{
 +		if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull())
 +		{
 +			// If we're in a local cache or we didn't actually receive any new data,
 +			// or we failed to load anything, skip
 +			mState = DONE;
 +			return false;
 +		}
 +		S32 datasize = mFormattedImage->getDataSize();
 +		if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed.
 +		{
 +			if(mHaveAllData)
 +			{
 +				mFileSize = datasize ;
 +			}
 +			else
 +			{
 +				mFileSize = datasize + 1 ; //flag not fully loaded.
 +			}
 +		}
 +		llassert_always(datasize);
 +		setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it
 +		U32 cache_priority = mWorkPriority;
 +		mWritten = FALSE;
 +		mState = WAIT_ON_WRITE;
 +		CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID);
 +		mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority,
 +																  mFormattedImage->getData(), datasize,
 +																  mFileSize, responder);
 +		// fall through
 +	}
 +	
 +	if (mState == WAIT_ON_WRITE)
 +	{
 +		if (writeToCacheComplete())
 +		{
 +			mState = DONE;
 +			// fall through
 +		}
 +		else
 +		{
 +			if (mDesiredDiscard < mDecodedDiscard)
 +			{
 +				// We're waiting for this write to complete before we can receive more data
 +				// (we can't touch mFormattedImage until the write completes)
 +				// Prioritize the write
 +				mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle);
 +			}
 +			return false;
 +		}
 +	}
 +
 +	if (mState == DONE)
 +	{
 +		if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard)
 +		{
 +			// More data was requested, return to INIT
 +			mState = INIT;
 +			setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +			return false;
 +		}
 +		else
 +		{
 +			setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority);
 +			return true;
 +		}
 +	}
 +	
 +	return false;
 +}
 +
 +// Called from MAIN thread
 +void LLTextureFetchWorker::endWork(S32 param, bool aborted)
 +{
 +	if (mDecodeHandle != 0)
 +	{
 +		mFetcher->mImageDecodeThread->abortRequest(mDecodeHandle, false);
 +		mDecodeHandle = 0;
 +	}
 +	mFormattedImage = NULL;
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +// virtual
 +void LLTextureFetchWorker::finishWork(S32 param, bool completed)
 +{
 +	// The following are required in case the work was aborted
 +	if (mCacheReadHandle != LLTextureCache::nullHandle())
 +	{
 +		mFetcher->mTextureCache->readComplete(mCacheReadHandle, true);
 +		mCacheReadHandle = LLTextureCache::nullHandle();
 +	}
 +	if (mCacheWriteHandle != LLTextureCache::nullHandle())
 +	{
 +		mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true);
 +		mCacheWriteHandle = LLTextureCache::nullHandle();
 +	}
 +}
 +
 +// virtual
 +bool LLTextureFetchWorker::deleteOK()
 +{
 +	bool delete_ok = true;
 +	// Allow any pending reads or writes to complete
 +	if (mCacheReadHandle != LLTextureCache::nullHandle())
 +	{
 +		if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true))
 +		{
 +			mCacheReadHandle = LLTextureCache::nullHandle();
 +		}
 +		else
 +		{
 +			delete_ok = false;
 +		}
 +	}
 +	if (mCacheWriteHandle != LLTextureCache::nullHandle())
 +	{
 +		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
 +		{
 +			mCacheWriteHandle = LLTextureCache::nullHandle();
 +		}
 +		else
 +		{
 +			delete_ok = false;
 +		}
 +	}
 +
 +	if ((haveWork() &&
 +		 // not ok to delete from these states
 +		 ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE))))
 +	{
 +		delete_ok = false;
 +	}
 +	
 +	return delete_ok;
 +}
 +
 +void LLTextureFetchWorker::removeFromCache()
 +{
 +	if (!mInLocalCache)
 +	{
 +		mFetcher->mTextureCache->removeFromCache(mID);
 +	}
 +}
 +
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +bool LLTextureFetchWorker::processSimulatorPackets()
 +{
 +	if (mFormattedImage.isNull() || mRequestedSize < 0)
 +	{
 +		// not sure how we got here, but not a valid state, abort!
 +		llassert_always(mDecodeHandle == 0);
 +		mFormattedImage = NULL;
 +		return true;
 +	}
 +	
 +	if (mLastPacket >= mFirstPacket)
 +	{
 +		S32 buffer_size = mFormattedImage->getDataSize();
 +		for (S32 i = mFirstPacket; i<=mLastPacket; i++)
 +		{
 +			llassert_always(mPackets[i]);
 +			buffer_size += mPackets[i]->mSize;
 +		}
 +		bool have_all_data = mLastPacket >= mTotalPackets-1;
 +		if (mRequestedSize <= 0)
 +		{
 +			// We received a packed but haven't requested anything yet (edge case)
 +			// Return true (we're "done") since we didn't request anything
 +			return true;
 +		}
 +		if (buffer_size >= mRequestedSize || have_all_data)
 +		{
 +			/// We have enough (or all) data
 +			if (have_all_data)
 +			{
 +				mHaveAllData = TRUE;
 +			}
 +			S32 cur_size = mFormattedImage->getDataSize();
 +			if (buffer_size > cur_size)
 +			{
 +				/// We have new data
 +				U8* buffer = new U8[buffer_size];
 +				S32 offset = 0;
 +				if (cur_size > 0 && mFirstPacket > 0)
 +				{
 +					memcpy(buffer, mFormattedImage->getData(), cur_size);
 +					offset = cur_size;
 +				}
 +				for (S32 i=mFirstPacket; i<=mLastPacket; i++)
 +				{
 +					memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
 +					offset += mPackets[i]->mSize;
 +				}
 +				// NOTE: setData releases current data
 +				mFormattedImage->setData(buffer, buffer_size);
 +			}
 +			mLoadedDiscard = mRequestedDiscard;
 +			return true;
 +		}
 +	}
 +	return false;
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels,
 +										   const LLIOPipe::buffer_ptr_t& buffer,
 +										   bool partial, bool success)
 +{
 +	S32 data_size = 0 ;
 +
 +	LLMutexLock lock(&mWorkMutex);
 +
 +	if (mState != WAIT_HTTP_REQ)
 +	{
 +		llwarns << "callbackHttpGet for unrequested fetch worker: " << mID
 +				<< " req=" << mSentRequest << " state= " << mState << llendl;
 +		return data_size;
 +	}
 +	if (mLoaded)
 +	{
 +		llwarns << "Duplicate callback for " << mID.asString() << llendl;
 +		return data_size ; // ignore duplicate callback
 +	}
 +	if (success)
 +	{
 +		// get length of stream:
 +		data_size = buffer->countAfter(channels.in(), NULL);		
 +	
 +		LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL;
 +		if (data_size > 0)
 +		{
 +			// *TODO: set the formatted image data here directly to avoid the copy
 +			mBuffer = new U8[data_size];
 +			buffer->readAfter(channels.in(), NULL, mBuffer, data_size);
 +			mBufferSize += data_size;
 +			if (data_size < mRequestedSize && mRequestedDiscard == 0)
 +			{
 +				mHaveAllData = TRUE;
 +			}
 +			else if (data_size > mRequestedSize)
 +			{
 +				// *TODO: This shouldn't be happening any more
 +				llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl;
 +				mHaveAllData = TRUE;
 +				llassert_always(mDecodeHandle == 0);
 +				mFormattedImage = NULL; // discard any previous data we had
 +				mBufferSize = data_size;
 +			}
 +		}
 +		else
 +		{
 +			// We requested data but received none (and no error),
 +			// so presumably we have all of it
 +			mHaveAllData = TRUE;
 +		}
 +		mRequestedSize = data_size;
 +	}
 +	else
 +	{
 +		mRequestedSize = -1; // error
 +	}
 +	mLoaded = TRUE;
 +	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +
 +	return data_size ;
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image,
 +											 S32 imagesize, BOOL islocal)
 +{
 +	LLMutexLock lock(&mWorkMutex);
 +	if (mState != LOAD_FROM_TEXTURE_CACHE)
 +	{
 +// 		llwarns << "Read callback for " << mID << " with state = " << mState << llendl;
 +		return;
 +	}
 +	if (success)
 +	{
 +		llassert_always(imagesize >= 0);
 +		mFileSize = imagesize;
 +		mFormattedImage = image;
 +		mImageCodec = image->getCodec();
 +		mInLocalCache = islocal;
 +		if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize)
 +		{
 +			mHaveAllData = TRUE;
 +		}
 +	}
 +	mLoaded = TRUE;
 +	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +}
 +
 +void LLTextureFetchWorker::callbackCacheWrite(bool success)
 +{
 +	LLMutexLock lock(&mWorkMutex);
 +	if (mState != WAIT_ON_WRITE)
 +	{
 +// 		llwarns << "Write callback for " << mID << " with state = " << mState << llendl;
 +		return;
 +	}
 +	mWritten = TRUE;
 +	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux)
 +{
 +	LLMutexLock lock(&mWorkMutex);
 +	if (mDecodeHandle == 0)
 +	{
 +		return; // aborted, ignore
 +	}
 +	if (mState != DECODE_IMAGE_UPDATE)
 +	{
 +// 		llwarns << "Decode callback for " << mID << " with state = " << mState << llendl;
 +		mDecodeHandle = 0;
 +		return;
 +	}
 +	llassert_always(mFormattedImage.notNull());
 +	
 +	mDecodeHandle = 0;
 +	if (success)
 +	{
 +		llassert_always(raw);
 +		mRawImage = raw;
 +		mAuxImage = aux;
 +		mDecodedDiscard = mFormattedImage->getDiscardLevel();
 + 		LL_DEBUGS("Texture") << mID << ": Decode Finished. Discard: " << mDecodedDiscard
 +							 << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
 +	}
 +	else
 +	{
 +		llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl;
 +		removeFromCache();
 +		mDecodedDiscard = -1; // Redundant, here for clarity and paranoia
 +	}
 +	mDecoded = TRUE;
 +// 	llinfos << mID << " : DECODE COMPLETE " << llendl;
 +	setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority);
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +bool LLTextureFetchWorker::writeToCacheComplete()
 +{
 +	// Complete write to cache
 +	if (mCacheWriteHandle != LLTextureCache::nullHandle())
 +	{
 +		if (!mWritten)
 +		{
 +			return false;
 +		}
 +		if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle))
 +		{
 +			mCacheWriteHandle = LLTextureCache::nullHandle();
 +		}
 +		else
 +		{
 +			return false;
 +		}
 +	}
 +	return true;
 +}
 +
 +
 +//////////////////////////////////////////////////////////////////////////////
 +//////////////////////////////////////////////////////////////////////////////
 +// public
 +
 +LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode)
 +	: LLWorkerThread("TextureFetch", threaded),
 +	  mDebugCount(0),
 +	  mDebugPause(FALSE),
 +	  mPacketCount(0),
 +	  mBadPacketCount(0),
 +	  mQueueMutex(getAPRPool()),
 +	  mNetworkQueueMutex(getAPRPool()),
 +	  mTextureCache(cache),
 +	  mImageDecodeThread(imagedecodethread),
 +	  mTextureBandwidth(0),
 +	  mHTTPTextureBits(0),
 +	  mTotalHTTPRequests(0),
 +	  mCurlGetRequest(NULL),
 +	  mQAMode(qa_mode)
 +{
 +	mCurlPOSTRequestCount = 0;
 +	mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS");
 +	mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold"));
 +}
 +
 +LLTextureFetch::~LLTextureFetch()
 +{
 +	clearDeleteList() ;
 +
 +	while (! mCommands.empty())
 +	{
 +		TFRequest * req(mCommands.front());
 +		mCommands.erase(mCommands.begin());
 +		delete req;
 +	}
 +	
 +	// ~LLQueuedThread() called here
 +}
 +
 +bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority,
 +								   S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http)
 +{
 +	if (mDebugPause)
 +	{
 +		return false;
 +	}
 +	
 +	LLTextureFetchWorker* worker = getWorker(id) ;
 +	if (worker)
 +	{
 +		if (worker->mHost != host)
 +		{
 +			llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: "
 +					<< host << " != " << worker->mHost << llendl;
 +			removeRequest(worker, true);
 +			worker = NULL;
 +			return false;
 +		}
 +	}
 +
 +	S32 desired_size;
 +	std::string exten = gDirUtilp->getExtension(url);
 +	if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C))
 +	{
 +		// Only do partial requests for J2C at the moment
 +		desired_size = MAX_IMAGE_DATA_SIZE;
 +		desired_discard = 0;
 +	}
 +	else if (desired_discard == 0)
 +	{
 +		// if we want the entire image, and we know its size, then get it all
 +		// (calcDataSizeJ2C() below makes assumptions about how the image
 +		// was compressed - this code ensures that when we request the entire image,
 +		// we really do get it.)
 +		desired_size = MAX_IMAGE_DATA_SIZE;
 +	}
 +	else if (w*h*c > 0)
 +	{
 +		// If the requester knows the dimensions of the image,
 +		// this will calculate how much data we need without having to parse the header
 +
 +		desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard);
 +	}
 +	else
 +	{
 +		desired_size = TEXTURE_CACHE_ENTRY_SIZE;
 +		desired_discard = MAX_DISCARD_LEVEL;
 +	}
 +
 +	
 +	if (worker)
 +	{
 +		if (worker->wasAborted())
 +		{
 +			return false; // need to wait for previous aborted request to complete
 +		}
 +		worker->lockWorkMutex();
 +		worker->mActiveCount++;
 +		worker->mNeedsAux = needs_aux;
 +		worker->setImagePriority(priority);
 +		worker->setDesiredDiscard(desired_discard, desired_size);
 +		worker->setCanUseHTTP(can_use_http) ;
 +		if (!worker->haveWork())
 +		{
 +			worker->mState = LLTextureFetchWorker::INIT;
 +			worker->unlockWorkMutex();
 +
 +			worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
 +		}
 +		else
 +		{
 +			worker->unlockWorkMutex();
 +		}
 +	}
 +	else
 +	{
 +		worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size);
 +		lockQueue() ;
 +		mRequestMap[id] = worker;
 +		unlockQueue() ;
 +
 +		worker->lockWorkMutex();
 +		worker->mActiveCount++;
 +		worker->mNeedsAux = needs_aux;
 +		worker->setCanUseHTTP(can_use_http) ;
 +		worker->unlockWorkMutex();
 +	}
 +	
 +// 	llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl;
 +	return true;
 +}
 +
 +// protected
 +void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker)
 +{
 +	lockQueue() ;
 +	bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ;
 +	unlockQueue() ;
 +
 +	LLMutexLock lock(&mNetworkQueueMutex);
 +	if (in_request_map)
 +	{
 +		// only add to the queue if in the request map
 +		// i.e. a delete has not been requested
 +		mNetworkQueue.insert(worker->mID);
 +	}
 +	for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
 +		 iter1 != mCancelQueue.end(); ++iter1)
 +	{
 +		iter1->second.erase(worker->mID);
 +	}
 +}
 +
 +void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel)
 +{
 +	LLMutexLock lock(&mNetworkQueueMutex);
 +	size_t erased = mNetworkQueue.erase(worker->mID);
 +	if (cancel && erased > 0)
 +	{
 +		mCancelQueue[worker->mHost].insert(worker->mID);
 +	}
 +}
 +
 +// protected
 +void LLTextureFetch::addToHTTPQueue(const LLUUID& id)
 +{
 +	LLMutexLock lock(&mNetworkQueueMutex);
 +	mHTTPTextureQueue.insert(id);
 +	mTotalHTTPRequests++;
 +}
 +
 +void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size)
 +{
 +	LLMutexLock lock(&mNetworkQueueMutex);
 +	mHTTPTextureQueue.erase(id);
 +	mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits	
 +}
 +
 +void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel)
 +{
 +	lockQueue() ;
 +	LLTextureFetchWorker* worker = getWorkerAfterLock(id);
 +	if (worker)
 +	{		
 +		size_t erased_1 = mRequestMap.erase(worker->mID);
 +		unlockQueue() ;
 +
 +		llassert_always(erased_1 > 0) ;
 +
 +		removeFromNetworkQueue(worker, cancel);
 +		llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
 +
 +		worker->scheduleDelete();	
 +	}
 +	else
 +	{
 +		unlockQueue() ;
 +	}
 +}
 +
 +void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel)
 +{
 +	lockQueue() ;
 +	size_t erased_1 = mRequestMap.erase(worker->mID);
 +	unlockQueue() ;
 +
 +	llassert_always(erased_1 > 0) ;
 +	removeFromNetworkQueue(worker, cancel);
 +	llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ;
 +
 +	worker->scheduleDelete();	
 +}
 +
 +S32 LLTextureFetch::getNumRequests() 
 +{ 
 +	lockQueue() ;
 +	S32 size = (S32)mRequestMap.size(); 
 +	unlockQueue() ;
 +
 +	return size ;
 +}
 +
 +S32 LLTextureFetch::getNumHTTPRequests() 
 +{ 
 +	mNetworkQueueMutex.lock() ;
 +	S32 size = (S32)mHTTPTextureQueue.size(); 
 +	mNetworkQueueMutex.unlock() ;
 +
 +	return size ;
 +}
 +
 +U32 LLTextureFetch::getTotalNumHTTPRequests()
 +{
 +	mNetworkQueueMutex.lock() ;
 +	U32 size = mTotalHTTPRequests ;
 +	mNetworkQueueMutex.unlock() ;
 +
 +	return size ;
 +}
 +
 +// call lockQueue() first!
 +LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id)
 +{
 +	LLTextureFetchWorker* res = NULL;
 +	map_t::iterator iter = mRequestMap.find(id);
 +	if (iter != mRequestMap.end())
 +	{
 +		res = iter->second;
 +	}
 +	return res;
 +}
 +
 +LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
 +{
 +	LLMutexLock lock(&mQueueMutex) ;
 +
 +	return getWorkerAfterLock(id) ;
 +}
 +
 +
 +bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level,
 +										LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux)
 +{
 +	bool res = false;
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	if (worker)
 +	{
 +		if (worker->wasAborted())
 +		{
 +			res = true;
 +		}
 +		else if (!worker->haveWork())
 +		{
 +			// Should only happen if we set mDebugPause...
 +			if (!mDebugPause)
 +			{
 +// 				llwarns << "Adding work for inactive worker: " << id << llendl;
 +				worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
 +			}
 +		}
 +		else if (worker->checkWork())
 +		{
 +			worker->lockWorkMutex();
 +			discard_level = worker->mDecodedDiscard;
 +			raw = worker->mRawImage;
 +			aux = worker->mAuxImage;
 +			res = true;
 +			LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL;
 +			worker->unlockWorkMutex();
 +		}
 +		else
 +		{
 +			worker->lockWorkMutex();
 +			if ((worker->mDecodedDiscard >= 0) &&
 +				(worker->mDecodedDiscard < discard_level || discard_level < 0) &&
 +				(worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE))
 +			{
 +				// Not finished, but data is ready
 +				discard_level = worker->mDecodedDiscard;
 +				raw = worker->mRawImage;
 +				aux = worker->mAuxImage;
 +			}
 +			worker->unlockWorkMutex();
 +		}
 +	}
 +	else
 +	{
 +		res = true;
 +	}
 +	return res;
 +}
 +
 +bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority)
 +{
 +	bool res = false;
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	if (worker)
 +	{
 +		worker->lockWorkMutex();
 +		worker->setImagePriority(priority);
 +		worker->unlockWorkMutex();
 +		res = true;
 +	}
 +	return res;
 +}
 +
 +// Replicates and expands upon the base class's
 +// getPending() implementation.  getPending() and
 +// runCondition() replicate one another's logic to
 +// an extent and are sometimes used for the same
 +// function (deciding whether or not to sleep/pause
 +// a thread).  So the implementations need to stay
 +// in step, at least until this can be refactored and
 +// the redundancy eliminated.
 +//
 +// May be called from any thread
 +
 +//virtual
 +S32 LLTextureFetch::getPending()
 +{
 +	S32 res;
 +	lockData();
 +    {
 +        LLMutexLock lock(&mQueueMutex);
 +        
 +        res = mRequestQueue.size();
 +        res += mCurlPOSTRequestCount;
 +        res += mCommands.size();
 +    }
 +	unlockData();
 +	return res;
 +}
 +
 +// virtual
 +bool LLTextureFetch::runCondition()
 +{
 +	// Caller is holding the lock on LLThread's condition variable.
 +	
 +	// LLQueuedThread, unlike its base class LLThread, makes this a
 +	// private method which is unfortunate.  I want to use it directly
 +	// but I'm going to have to re-implement the logic here (or change
 +	// declarations, which I don't want to do right now).
 +	//
 +	// Changes here may need to be reflected in getPending().
 +	
 +	bool have_no_commands(false);
 +	{
 +		LLMutexLock lock(&mQueueMutex);
 +		
 +		have_no_commands = mCommands.empty();
 +	}
 +	
 +    bool have_no_curl_requests(0 == mCurlPOSTRequestCount);
 +	
 +	return ! (have_no_commands
 +			  && have_no_curl_requests
 +			  && (mRequestQueue.empty() && mIdleThread));		// From base class
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs)
 +void LLTextureFetch::commonUpdate()
 +{
 +	// Run a cross-thread command, if any.
 +	cmdDoWork();
 +	
 +	// Update Curl on same thread as mCurlGetRequest was constructed
 +	S32 processed = mCurlGetRequest->process();
 +	if (processed > 0)
 +	{
 +		lldebugs << "processed: " << processed << " messages." << llendl;
 +	}
 +}
 +
 +
 +// MAIN THREAD
 +//virtual
 +S32 LLTextureFetch::update(U32 max_time_ms)
 +{
 +	static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS");
 +
 +	{
 +		mNetworkQueueMutex.lock() ;
 +		mMaxBandwidth = band_width ;
 +
 +		gTextureList.sTextureBits += mHTTPTextureBits ;
 +		mHTTPTextureBits = 0 ;
 +
 +		mNetworkQueueMutex.unlock() ;
 +	}
 +
 +	S32 res = LLWorkerThread::update(max_time_ms);
 +	
 +	if (!mDebugPause)
 +	{
 +		sendRequestListToSimulators();
 +	}
 +
 +	if (!mThreaded)
 +	{
 +		commonUpdate();
 +	}
 +
 +	return res;
 +}
 +
 +//called in the MAIN thread after the TextureCacheThread shuts down.
 +void LLTextureFetch::shutDownTextureCacheThread() 
 +{
 +	if(mTextureCache)
 +	{
 +		llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ;
 +		mTextureCache = NULL ;
 +	}
 +}
 +	
 +//called in the MAIN thread after the ImageDecodeThread shuts down.
 +void LLTextureFetch::shutDownImageDecodeThread() 
 +{
 +	if(mImageDecodeThread)
 +	{
 +		llassert_always(mImageDecodeThread->isQuitting() || mImageDecodeThread->isStopped()) ;
 +		mImageDecodeThread = NULL ;
 +	}
 +}
 +
 +// WORKER THREAD
 +void LLTextureFetch::startThread()
 +{
 +	// Construct mCurlGetRequest from Worker Thread
 +	mCurlGetRequest = new LLCurlRequest();
 +}
 +
 +// WORKER THREAD
 +void LLTextureFetch::endThread()
 +{
 +	// Destroy mCurlGetRequest from Worker Thread
 +	delete mCurlGetRequest;
 +	mCurlGetRequest = NULL;
 +}
 +
 +// WORKER THREAD
 +void LLTextureFetch::threadedUpdate()
 +{
 +	llassert_always(mCurlGetRequest);
 +	
 +	// Limit update frequency
 +	const F32 PROCESS_TIME = 0.05f; 
 +	static LLFrameTimer process_timer;
 +	if (process_timer.getElapsedTimeF32() < PROCESS_TIME)
 +	{
 +		return;
 +	}
 +	process_timer.reset();
 +	
 +	commonUpdate();
 +
 +#if 0
 +	const F32 INFO_TIME = 1.0f; 
 +	static LLFrameTimer info_timer;
 +	if (info_timer.getElapsedTimeF32() >= INFO_TIME)
 +	{
 +		S32 q = mCurlGetRequest->getQueued();
 +		if (q > 0)
 +		{
 +			llinfos << "Queued gets: " << q << llendl;
 +			info_timer.reset();
 +		}
 +	}
 +#endif
 +	
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +void LLTextureFetch::sendRequestListToSimulators()
 +{
 +	// All requests
 +	const F32 REQUEST_DELTA_TIME = 0.10f; // 10 fps
 +	
 +	// Sim requests
 +	const S32 IMAGES_PER_REQUEST = 50;
 +	const F32 SIM_LAZY_FLUSH_TIMEOUT = 10.0f; // temp
 +	const F32 MIN_REQUEST_TIME = 1.0f;
 +	const F32 MIN_DELTA_PRIORITY = 1000.f;
 +
 +	// Periodically, gather the list of textures that need data from the network
 +	// And send the requests out to the simulators
 +	static LLFrameTimer timer;
 +	if (timer.getElapsedTimeF32() < REQUEST_DELTA_TIME)
 +	{
 +		return;
 +	}
 +	timer.reset();
 +	
 +	// Send requests
 +	typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t;
 +	typedef std::map< LLHost, request_list_t > work_request_map_t;
 +	work_request_map_t requests;
 +	{
 +	LLMutexLock lock2(&mNetworkQueueMutex);
 +	for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); )
 +	{
 +		queue_t::iterator curiter = iter++;
 +		LLTextureFetchWorker* req = getWorker(*curiter);
 +		if (!req)
 +		{
 +			mNetworkQueue.erase(curiter);
 +			continue; // paranoia
 +		}
 +		if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) &&
 +			(req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR))
 +		{
 +			// We already received our URL, remove from the queue
 +			llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl;
 +			mNetworkQueue.erase(curiter);
 +			continue;
 +		}
 +		if (req->mID == mDebugID)
 +		{
 +			mDebugCount++; // for setting breakpoints
 +		}
 +		if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM &&
 +			req->mTotalPackets > 0 &&
 +			req->mLastPacket >= req->mTotalPackets-1)
 +		{
 +			// We have all the packets... make sure this is high priority
 +// 			req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority);
 +			continue;
 +		}
 +		F32 elapsed = req->mRequestedTimer.getElapsedTimeF32();
 +		{
 +			F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority);
 +			if ((req->mSimRequestedDiscard != req->mDesiredDiscard) ||
 +				(delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) ||
 +				(elapsed >= SIM_LAZY_FLUSH_TIMEOUT))
 +			{
 +				requests[req->mHost].insert(req);
 +			}
 +		}
 +	}
 +	}
 +
 +	for (work_request_map_t::iterator iter1 = requests.begin();
 +		 iter1 != requests.end(); ++iter1)
 +	{
 +		LLHost host = iter1->first;
 +		// invalid host = use agent host
 +		if (host == LLHost::invalid)
 +		{
 +			host = gAgent.getRegionHost();
 +		}
 +
 +		S32 sim_request_count = 0;
 +		
 +		for (request_list_t::iterator iter2 = iter1->second.begin();
 +			 iter2 != iter1->second.end(); ++iter2)
 +		{
 +			LLTextureFetchWorker* req = *iter2;
 +			if (gMessageSystem)
 +			{
 +				if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM)
 +				{
 +					// Initialize packet data based on data read from cache
 +					req->lockWorkMutex();
 +					req->setupPacketData();
 +					req->unlockWorkMutex();
 +				}
 +				if (0 == sim_request_count)
 +				{
 +					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
 +					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
 +					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 +					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 +				}
 +				S32 packet = req->mLastPacket + 1;
 +				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
 +				gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
 +				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mDesiredDiscard);
 +				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority);
 +				gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
 +				gMessageSystem->addU8Fast(_PREHASH_Type, req->mType);
 +// 				llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard
 +// 						<< " Packet: " << packet << " Priority: " << req->mImagePriority << llendl;
 +
 +				static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
 +				static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
 +				if (log_to_viewer_log || log_to_sim)
 +				{
 +					mTextureInfo.setRequestStartTime(req->mID, LLTimer::getTotalTime());
 +					mTextureInfo.setRequestOffset(req->mID, 0);
 +					mTextureInfo.setRequestSize(req->mID, 0);
 +					mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP);
 +				}
 +
 +				req->lockWorkMutex();
 +				req->mSentRequest = LLTextureFetchWorker::SENT_SIM;
 +				req->mSimRequestedDiscard = req->mDesiredDiscard;
 +				req->mRequestedPriority = req->mImagePriority;
 +				req->mRequestedTimer.reset();
 +				req->unlockWorkMutex();
 +				sim_request_count++;
 +				if (sim_request_count >= IMAGES_PER_REQUEST)
 +				{
 +// 					llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
 +
 +					gMessageSystem->sendSemiReliable(host, NULL, NULL);
 +					sim_request_count = 0;
 +				}
 +			}
 +		}
 +		if (gMessageSystem && sim_request_count > 0 && sim_request_count < IMAGES_PER_REQUEST)
 +		{
 +// 			llinfos << "REQUESTING " << sim_request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl;
 +			gMessageSystem->sendSemiReliable(host, NULL, NULL);
 +			sim_request_count = 0;
 +		}
 +	}
 +	
 +	// Send cancelations
 +	{
 +	LLMutexLock lock2(&mNetworkQueueMutex);
 +	if (gMessageSystem && !mCancelQueue.empty())
 +	{
 +		for (cancel_queue_t::iterator iter1 = mCancelQueue.begin();
 +			 iter1 != mCancelQueue.end(); ++iter1)
 +		{
 +			LLHost host = iter1->first;
 +			if (host == LLHost::invalid)
 +			{
 +				host = gAgent.getRegionHost();
 +			}
 +			S32 request_count = 0;
 +			for (queue_t::iterator iter2 = iter1->second.begin();
 +				 iter2 != iter1->second.end(); ++iter2)
 +			{
 +				if (0 == request_count)
 +				{
 +					gMessageSystem->newMessageFast(_PREHASH_RequestImage);
 +					gMessageSystem->nextBlockFast(_PREHASH_AgentData);
 +					gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
 +					gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
 +				}
 +				gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
 +				gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2);
 +				gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1);
 +				gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0);
 +				gMessageSystem->addU32Fast(_PREHASH_Packet, 0);
 +				gMessageSystem->addU8Fast(_PREHASH_Type, 0);
 +// 				llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl;
 +
 +				request_count++;
 +				if (request_count >= IMAGES_PER_REQUEST)
 +				{
 +					gMessageSystem->sendSemiReliable(host, NULL, NULL);
 +					request_count = 0;
 +				}
 +			}
 +			if (request_count > 0 && request_count < IMAGES_PER_REQUEST)
 +			{
 +				gMessageSystem->sendSemiReliable(host, NULL, NULL);
 +			}
 +		}
 +		mCancelQueue.clear();
 +	}
 +	}
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
 +{
 +	mRequestedTimer.reset();
 +	if (index >= mTotalPackets)
 +	{
 +// 		llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl;
 +		return false;
 +	}
 +	if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE)
 +	{
 +// 		llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl;
 +		return false;
 +	}
 +	
 +	if (index >= (S32)mPackets.size())
 +	{
 +		mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
 +	}
 +	else if (mPackets[index] != NULL)
 +	{
 +// 		llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl;
 +		return false;
 +	}
 +
 +	mPackets[index] = new PacketData(data, size);
 +	while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
 +	{
 +		++mLastPacket;
 +	}
 +	return true;
 +}
 +
 +bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes,
 +										U16 data_size, U8* data)
 +{
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	bool res = true;
 +
 +	++mPacketCount;
 +	
 +	if (!worker)
 +	{
 +// 		llwarns << "Received header for non active worker: " << id << llendl;
 +		res = false;
 +	}
 +	else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK ||
 +			 worker->mSentRequest != LLTextureFetchWorker::SENT_SIM)
 +	{
 +// 		llwarns << "receiveImageHeader for worker: " << id
 +// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState]
 +// 				<< " sent: " << worker->mSentRequest << llendl;
 +		res = false;
 +	}
 +	else if (worker->mLastPacket != -1)
 +	{
 +		// check to see if we've gotten this packet before
 +// 		llwarns << "Received duplicate header for: " << id << llendl;
 +		res = false;
 +	}
 +	else if (!data_size)
 +	{
 +// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
 +		res = false;
 +	}
 +	if (!res)
 +	{
 +		++mBadPacketCount;
 +		mNetworkQueueMutex.lock() ;
 +		mCancelQueue[host].insert(id);
 +		mNetworkQueueMutex.unlock() ;
 +		return false;
 +	}
 +
 +	worker->lockWorkMutex();
 +
 +	//	Copy header data into image object
 +	worker->mImageCodec = codec;
 +	worker->mTotalPackets = packets;
 +	worker->mFileSize = (S32)totalbytes;	
 +	llassert_always(totalbytes > 0);
 +	llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize);
 +	res = worker->insertPacket(0, data, data_size);
 +	worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
 +	worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
 +	worker->unlockWorkMutex();
 +	return res;
 +}
 +
 +bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
 +{
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	bool res = true;
 +
 +	++mPacketCount;
 +	
 +	if (!worker)
 +	{
 +// 		llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl;
 +		res = false;
 +	}
 +	else if (worker->mLastPacket == -1)
 +	{
 +// 		llwarns << "Received packet " << packet_num << " before header for: " << id << llendl;
 +		res = false;
 +	}
 +	else if (!data_size)
 +	{
 +// 		llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl;
 +		res = false;
 +	}
 +	if (!res)
 +	{
 +		++mBadPacketCount;
 +		mNetworkQueueMutex.lock() ;
 +		mCancelQueue[host].insert(id);
 +		mNetworkQueueMutex.unlock() ;
 +		return false;
 +	}
 +
 +	worker->lockWorkMutex();
 +	
 +	res = worker->insertPacket(packet_num, data, data_size);
 +	
 +	if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) ||
 +		(worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK))
 +	{
 +		worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority);
 +		worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR;
 +	}
 +	else
 +	{
 +// 		llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id
 +// 				<< " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl;
 +		removeFromNetworkQueue(worker, true); // failsafe
 +	}
 +
 +	if(packet_num >= (worker->mTotalPackets - 1))
 +	{
 +		static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog");
 +		static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator");
 +
 +		if (log_to_viewer_log || log_to_sim)
 +		{
 +			U64 timeNow = LLTimer::getTotalTime();
 +			mTextureInfo.setRequestSize(id, worker->mFileSize);
 +			mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow);
 +		}
 +	}
 +	worker->unlockWorkMutex();
 +
 +	return res;
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id)
 +{
 +	BOOL from_cache = FALSE ;
 +
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	if (worker)
 +	{
 +		worker->lockWorkMutex() ;
 +		from_cache = worker->mInLocalCache ;
 +		worker->unlockWorkMutex() ;
 +	}
 +
 +	return from_cache ;
 +}
 +
 +S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p,
 +								  U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http)
 +{
 +	S32 state = LLTextureFetchWorker::INVALID;
 +	F32 data_progress = 0.0f;
 +	F32 requested_priority = 0.0f;
 +	F32 fetch_dtime = 999999.f;
 +	F32 request_dtime = 999999.f;
 +	U32 fetch_priority = 0;
 +	
 +	LLTextureFetchWorker* worker = getWorker(id);
 +	if (worker && worker->haveWork())
 +	{
 +		worker->lockWorkMutex();
 +		state = worker->mState;
 +		fetch_dtime = worker->mFetchTimer.getElapsedTimeF32();
 +		request_dtime = worker->mRequestedTimer.getElapsedTimeF32();
 +		if (worker->mFileSize > 0)
 +		{
 +			if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR)
 +			{
 +				S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE;
 +				data_size = llmax(data_size, 0);
 +				data_progress = (F32)data_size / (F32)worker->mFileSize;
 +			}
 +			else if (worker->mFormattedImage.notNull())
 +			{
 +				data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize;
 +			}
 +		}
 +		if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ)
 +		{
 +			requested_priority = worker->mRequestedPriority;
 +		}
 +		else
 +		{
 +			requested_priority = worker->mImagePriority;
 +		}
 +		fetch_priority = worker->getPriority();
 +		can_use_http = worker->getCanUseHTTP() ;
 +		worker->unlockWorkMutex();
 +	}
 +	data_progress_p = data_progress;
 +	requested_priority_p = requested_priority;
 +	fetch_priority_p = fetch_priority;
 +	fetch_dtime_p = fetch_dtime;
 +	request_dtime_p = request_dtime;
 +	return state;
 +}
 +
 +void LLTextureFetch::dump()
 +{
 +	llinfos << "LLTextureFetch REQUESTS:" << llendl;
 +	for (request_queue_t::iterator iter = mRequestQueue.begin();
 +		 iter != mRequestQueue.end(); ++iter)
 +	{
 +		LLQueuedThread::QueuedRequest* qreq = *iter;
 +		LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq;
 +		LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass();
 +		llinfos << " ID: " << worker->mID
 +				<< " PRI: " << llformat("0x%08x",wreq->getPriority())
 +				<< " STATE: " << worker->sStateDescs[worker->mState]
 +				<< llendl;
 +	}
 +}
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +// cross-thread command methods
 +
 +void LLTextureFetch::commandSetRegion(U64 region_handle)
 +{
 +	TFReqSetRegion * req = new TFReqSetRegion(region_handle);
 +
 +	cmdEnqueue(req);
 +}
 +
 +void LLTextureFetch::commandSendMetrics(const std::string & caps_url,
 +										const LLUUID & session_id,
 +										const LLUUID & agent_id,
 +										LLViewerAssetStats * main_stats)
 +{
 +	TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats);
 +
 +	cmdEnqueue(req);
 +}
 +
 +void LLTextureFetch::commandDataBreak()
 +{
 +	// The pedantically correct way to implement this is to create a command
 +	// request object in the above fashion and enqueue it.  However, this is
 +	// simple data of an advisorial not operational nature and this case
 +	// of shared-write access is tolerable.
 +
 +	LLTextureFetch::svMetricsDataBreak = true;
 +}
 +
 +void LLTextureFetch::cmdEnqueue(TFRequest * req)
 +{
 +	lockQueue();
 +	mCommands.push_back(req);
 +	unlockQueue();
 +
 +	unpause();
 +}
 +
 +LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue()
 +{
 +	TFRequest * ret = 0;
 +	
 +	lockQueue();
 +	if (! mCommands.empty())
 +	{
 +		ret = mCommands.front();
 +		mCommands.erase(mCommands.begin());
 +	}
 +	unlockQueue();
 +
 +	return ret;
 +}
 +
 +void LLTextureFetch::cmdDoWork()
 +{
 +	if (mDebugPause)
 +	{
 +		return;  // debug: don't do any work
 +	}
 +
 +	TFRequest * req = cmdDequeue();
 +	if (req)
 +	{
 +		// One request per pass should really be enough for this.
 +		req->doWork(this);
 +		delete req;
 +	}
 +}
 +
 +
 +//////////////////////////////////////////////////////////////////////////////
 +
 +// Private (anonymous) class methods implementing the command scheme.
 +
 +namespace
 +{
 +
 +/**
 + * Implements the 'Set Region' command.
 + *
 + * Thread:  Thread1 (TextureFetch)
 + */
 +bool
 +TFReqSetRegion::doWork(LLTextureFetch *)
 +{
 +	LLViewerAssetStatsFF::set_region_thread1(mRegionHandle);
 +
 +	return true;
 +}
 +
 +
 +TFReqSendMetrics::~TFReqSendMetrics()
 +{
 +	delete mMainStats;
 +	mMainStats = 0;
 +}
 +
 +
 +/**
 + * Implements the 'Send Metrics' command.  Takes over
 + * ownership of the passed LLViewerAssetStats pointer.
 + *
 + * Thread:  Thread1 (TextureFetch)
 + */
 +bool
 +TFReqSendMetrics::doWork(LLTextureFetch * fetcher)
 +{
 +	/*
 +	 * HTTP POST responder.  Doesn't do much but tries to
 +	 * detect simple breaks in recording the metrics stream.
 +	 *
 +	 * The 'volatile' modifiers don't indicate signals,
 +	 * mmap'd memory or threads, really.  They indicate that
 +	 * the referenced data is part of a pseudo-closure for
 +	 * this responder rather than being required for correct
 +	 * operation.
 +     *
 +     * We don't try very hard with the POST request.  We give
 +     * it one shot and that's more-or-less it.  With a proper
 +     * refactoring of the LLQueuedThread usage, these POSTs
 +     * could be put in a request object and made more reliable.
 +	 */
 +	class lcl_responder : public LLCurl::Responder
 +	{
 +	public:
 +		lcl_responder(LLTextureFetch * fetcher,
 +					  S32 expected_sequence,
 +                      volatile const S32 & live_sequence,
 +                      volatile bool & reporting_break,
 +					  volatile bool & reporting_started)
 +			: LLCurl::Responder(),
 +			  mFetcher(fetcher),
 +              mExpectedSequence(expected_sequence),
 +              mLiveSequence(live_sequence),
 +			  mReportingBreak(reporting_break),
 +			  mReportingStarted(reporting_started)
 +			{
 +                mFetcher->incrCurlPOSTCount();
 +            }
 +        
 +        ~lcl_responder()
 +            {
 +                mFetcher->decrCurlPOSTCount();
 +            }
 +
 +		// virtual
 +		void error(U32 status_num, const std::string & reason)
 +			{
 +                if (mLiveSequence == mExpectedSequence)
 +                {
 +                    mReportingBreak = true;
 +                }
 +				LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service.  Reason:  "
 +									<< reason << LL_ENDL;
 +			}
 +
 +		// virtual
 +		void result(const LLSD & content)
 +			{
 +                if (mLiveSequence == mExpectedSequence)
 +                {
 +                    mReportingBreak = false;
 +                    mReportingStarted = true;
 +                }
 +			}
 +
 +	private:
 +		LLTextureFetch * mFetcher;
 +        S32 mExpectedSequence;
 +        volatile const S32 & mLiveSequence;
 +		volatile bool & mReportingBreak;
 +		volatile bool & mReportingStarted;
 +
 +	}; // class lcl_responder
 +	
 +	if (! gViewerAssetStatsThread1)
 +		return true;
 +
 +	static volatile bool reporting_started(false);
 +	static volatile S32 report_sequence(0);
 +    
 +	// We've taken over ownership of the stats copy at this
 +	// point.  Get a working reference to it for merging here
 +	// but leave it in 'this'.  Destructor will rid us of it.
 +	LLViewerAssetStats & main_stats = *mMainStats;
 +
 +	// Merge existing stats into those from main, convert to LLSD
 +	main_stats.merge(*gViewerAssetStatsThread1);
 +	LLSD merged_llsd = main_stats.asLLSD(true);
 +
 +	// Add some additional meta fields to the content
 +	merged_llsd["session_id"] = mSessionID;
 +	merged_llsd["agent_id"] = mAgentID;
 +	merged_llsd["message"] = "ViewerAssetMetrics";					// Identifies the type of metrics
 +	merged_llsd["sequence"] = report_sequence;						// Sequence number
 +	merged_llsd["initial"] = ! reporting_started;					// Initial data from viewer
 +	merged_llsd["break"] = LLTextureFetch::svMetricsDataBreak;		// Break in data prior to this report
 +		
 +	// Update sequence number
 +	if (S32_MAX == ++report_sequence)
 +		report_sequence = 0;
 +
 +	// Limit the size of the stats report if necessary.
 +	merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd);
 +
 +	if (! mCapsURL.empty())
 +	{
 +		LLCurlRequest::headers_t headers;
 +		fetcher->getCurlRequest().post(mCapsURL,
 +									   headers,
 +									   merged_llsd,
 +									   new lcl_responder(fetcher,
 +														 report_sequence,
 +                                                         report_sequence,
 +                                                         LLTextureFetch::svMetricsDataBreak,
 +														 reporting_started));
 +	}
 +	else
 +	{
 +		LLTextureFetch::svMetricsDataBreak = true;
 +	}
 +
 +	// In QA mode, Metrics submode, log the result for ease of testing
 +	if (fetcher->isQAMode())
 +	{
 +		LL_INFOS("Textures") << merged_llsd << LL_ENDL;
 +	}
 +
 +	gViewerAssetStatsThread1->reset();
 +
 +	return true;
 +}
 +
 +
 +bool
 +truncate_viewer_metrics(int max_regions, LLSD & metrics)
 +{
 +	static const LLSD::String reg_tag("regions");
 +	static const LLSD::String duration_tag("duration");
 +	
 +	LLSD & reg_map(metrics[reg_tag]);
 +	if (reg_map.size() <= max_regions)
 +	{
 +		return false;
 +	}
 +
 +	// Build map of region hashes ordered by duration
 +	typedef std::multimap<LLSD::Real, int> reg_ordered_list_t;
 +	reg_ordered_list_t regions_by_duration;
 +
 +	int ind(0);
 +	LLSD::array_const_iterator it_end(reg_map.endArray());
 +	for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind)
 +	{
 +		LLSD::Real duration = (*it)[duration_tag].asReal();
 +		regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind));
 +	}
 +
 +	// Build a replacement regions array with the longest-persistence regions
 +	LLSD new_region(LLSD::emptyArray());
 +	reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend());
 +	reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin());
 +	for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2)
 +	{
 +		new_region.append(reg_map[it2->second]);
 +	}
 +	reg_map = new_region;
 +	
 +	return true;
 +}
 +
 +} // end of anonymous namespace
 +
 +
 +
 diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index ad00a7ea36..d101da1f4b 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -79,6 +79,7 @@ public:  	void dump();  	S32 getNumRequests() ;  	S32 getNumHTTPRequests() ; +	U32 getTotalNumHTTPRequests() ;  	// Public for access by callbacks      S32 getPending(); @@ -183,6 +184,9 @@ private:  	U32 mHTTPTextureBits; +	//debug use +	U32 mTotalHTTPRequests ; +  	// Out-of-band cross-thread command queue.  This command queue  	// is logically tied to LLQueuedThread's list of  	// QueuedRequest instances and so must be covered by the diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index b9a15fd1f4..0115115a23 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -516,6 +516,7 @@ void LLGLTexMemBar::draw()  	S32 v_offset = (S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f);  	F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024);  	F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); +	U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests() ;  	//----------------------------------------------------------------------------  	LLGLSUIDefault gls_ui;  	LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); @@ -526,13 +527,13 @@ void LLGLTexMemBar::draw()  	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6,  											 text_color, LLFontGL::LEFT, LLFontGL::TOP); -	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB", +	text = llformat("GL Tot: %d/%d MB Bound: %d/%d MB Raw Tot: %d MB Bias: %.2f Cache: %.1f/%.1f MB Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d",  					total_mem,  					max_total_mem,  					bound_mem,  					max_bound_mem,  					LLImageRaw::sGlobalRawMemory >> 20,	discard_bias, -					cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded); +					cache_usage, cache_max_usage, total_texture_downloaded, total_object_downloaded, total_http_requests);  	//, cache_entries, cache_max_entries  	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b3642a2c1e..cc851e676b 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -366,11 +366,11 @@ void LLViewerInventoryItem::fetchFromServer(void) const  		{  		  if(gAgent.getID() != mPermissions.getOwner())  		    { -		      url = region->getCapability("FetchLib"); +		      url = region->getCapability("FetchLib2");  		    }  		  else  		    {	 -		      url = region->getCapability("FetchInventory"); +		      url = region->getCapability("FetchInventory2");  		    }  		}  		else @@ -648,7 +648,7 @@ bool LLViewerInventoryCategory::fetch()  		std::string url;  		if (gAgent.getRegion())  		{ -			url = gAgent.getRegion()->getCapability("WebFetchInventoryDescendents"); +			url = gAgent.getRegion()->getCapability("FetchInventoryDescendents2");  		}  		else  		{ @@ -660,7 +660,7 @@ bool LLViewerInventoryCategory::fetch()  		}  		else  		{	//Deprecated, but if we don't have a capability, use the old system. -			llinfos << "WebFetchInventoryDescendents capability not found.  Using deprecated UDP message." << llendl; +			llinfos << "FetchInventoryDescendents2 capability not found.  Using deprecated UDP message." << llendl;  			LLMessageSystem* msg = gMessageSystem;  			msg->newMessage("FetchInventoryDescendents");  			msg->nextBlock("AgentData"); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 433151860c..f16d8814dd 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1436,9 +1436,12 @@ void LLViewerMedia::proxyWindowClosed(const std::string &uuid)  // static  void LLViewerMedia::createSpareBrowserMediaSource()  { -	if(!sSpareBrowserMediaSource) +	// If we don't have a spare browser media source, create one. +	// However, if PluginAttachDebuggerToPlugins is set then don't spawn a spare +	// SLPlugin process in order to not be confused by an unrelated gdb terminal +	// popping up at the moment we start a media plugin. +	if (!sSpareBrowserMediaSource && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))  	{ -		// If we don't have a spare browser media source, create one.  		// The null owner will keep the browser plugin from fully initializing   		// (specifically, it keeps LLPluginClassMedia from negotiating a size change,   		// which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color) @@ -1694,7 +1697,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_  	LLPluginClassMedia* media_source = NULL;  	// HACK: we always try to keep a spare running webkit plugin around to improve launch times. -	if(plugin_basename == "media_plugin_webkit") +	// If a spare was already created before PluginAttachDebuggerToPlugins was set, don't use it. +	if(plugin_basename == "media_plugin_webkit" && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))  	{  		media_source = LLViewerMedia::getSpareBrowserMediaSource();  		if(media_source) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9e16bf2fbb..7cc04e0338 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -557,7 +557,7 @@ class LLAdvancedCheckConsole : public view_listener_t  			new_value = get_visibility( (void*)gDebugView->mMemoryView );  		}  #endif - +		  		return new_value;  	}  }; @@ -906,6 +906,10 @@ U32 info_display_from_string(std::string info_display)  	{  		return LLPipeline::RENDER_DEBUG_BATCH_SIZE;  	} +	else if ("update type" == info_display) +	{ +		return LLPipeline::RENDER_DEBUG_UPDATE_TYPE; +	}  	else if ("texture anim" == info_display)  	{  		return LLPipeline::RENDER_DEBUG_TEXTURE_ANIM; @@ -4197,9 +4201,9 @@ class LLObjectEnableReturn : public view_listener_t  					{  						virtual bool apply(LLViewerObject* obj)  						{ -							return (obj->isOverAgentOwnedLand() || -									obj->isOverGroupOwnedLand() || -									obj->permModify()); +							return  +								obj->permModify() || +								obj->isReturnable();  						}  					} func;  					const bool firstonly = true; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 7dc5d96689..6fc85a3944 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -171,31 +171,6 @@ const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] =  	FALSE	// ControlYourCamera  }; -// Extract channel and version from a string like "SL Web Viewer Beta 10.11.29.215604". -// (channel: "SL Web Viewer Beta", version: "10.11.29.215604") -static bool parse_version_info(const std::string& version_info, std::string& channel, std::string& ver) -{ -	size_t last_space = version_info.rfind(" "); -	channel = version_info; - -	if (last_space != std::string::npos) -	{ -		try -		{ -			ver = version_info.substr(last_space + 1); -			channel.replace(last_space, ver.length() + 1, ""); // strip version -		} -		catch (std::out_of_range) -		{ -			return false; -		} - -		return true; -	} - -	return false; -} -  bool friendship_offer_callback(const LLSD& notification, const LLSD& response)  {  	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -3848,31 +3823,6 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)  		return;  	} -	if (!gLastVersionChannel.empty()) -	{ -		std::string url = regionp->getCapability("ServerReleaseNotes"); -		if (url.empty()) -		{ -			// The capability hasn't arrived yet or is not supported, -			// fall back to parsing server version channel. -			std::string channel, ver; -			if (!parse_version_info(version_channel, channel, ver)) -			{ -				llwarns << "Failed to parse server version channel (" << version_channel << ")" << llendl; -			} - -			url = gSavedSettings.getString("ReleaseNotesURL"); -			LLSD args; -			args["CHANNEL"] = LLWeb::escapeURL(channel); -			args["VERSION"] = LLWeb::escapeURL(ver); -			LLStringUtil::format(url, args); -		} - -		LLSD args; -		args["URL"] = url; -		LLNotificationsUtil::add("ServerVersionChanged", args); -	} -  	gLastVersionChannel = version_channel;  } diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 1804fac1b3..090d3cadd4 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -210,7 +210,6 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe  	mLastInterpUpdateSecs(0.f),  	mLastMessageUpdateSecs(0.f),  	mLatestRecvPacketID(0), -	mCircuitPacketCount(0),  	mData(NULL),  	mAudioSourcep(NULL),  	mAudioGain(1.f), @@ -234,7 +233,9 @@ LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRe  	mState(0),  	mMedia(NULL),  	mClickAction(0), -	mAttachmentItemID(LLUUID::null) +	mAttachmentItemID(LLUUID::null), +	mLastUpdateType(OUT_UNKNOWN), +	mLastUpdateCached(FALSE)  {  	if (!is_global)  	{ @@ -516,20 +517,23 @@ void LLViewerObject::setNameValueList(const std::string& name_value_list)  // This method returns true if the object is over land owned by the  // agent. -BOOL LLViewerObject::isOverAgentOwnedLand() const +bool LLViewerObject::isReturnable()  { -	return mRegionp -		&& mRegionp->getParcelOverlay() -		&& mRegionp->getParcelOverlay()->isOwnedSelf(getPositionRegion()); -} +	if (isAttachment()) +	{ +		return false; +	} +	std::vector<LLBBox> boxes; +	boxes.push_back(LLBBox(getPositionRegion(), getRotationRegion(), getScale() * -0.5f, getScale() * 0.5f).getAxisAligned()); +	for (child_list_t::iterator iter = mChildList.begin(); +		 iter != mChildList.end(); iter++) +	{ +		LLViewerObject* child = *iter; +		boxes.push_back(LLBBox(child->getPositionRegion(), child->getRotationRegion(), child->getScale() * -0.5f, child->getScale() * 0.5f).getAxisAligned()); +	} -// This method returns true if the object is over land owned by the -// agent. -BOOL LLViewerObject::isOverGroupOwnedLand() const -{ -	return mRegionp  -		&& mRegionp->getParcelOverlay() -		&& mRegionp->getParcelOverlay()->isOwnedGroup(getPositionRegion()); +	return mRegionp +		&& mRegionp->objectIsReturnable(getPositionRegion(), boxes);  }  BOOL LLViewerObject::setParent(LLViewerObject* parent) @@ -1879,7 +1883,6 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,  	}  	mLatestRecvPacketID = packet_id; -	mCircuitPacketCount = 0;  	// Set the change flags for scale  	if (new_scale != getScale()) @@ -2202,7 +2205,8 @@ void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt)  		LLVector3 new_pos = (vel + (0.5f * (dt-PHYSICS_TIMESTEP)) * accel) * dt;	  		LLVector3 new_v = accel * dt; -		if (time_since_last_update > sPhaseOutUpdateInterpolationTime) +		if (time_since_last_update > sPhaseOutUpdateInterpolationTime && +			sPhaseOutUpdateInterpolationTime > 0.0)  		{	// Haven't seen a viewer update in a while, check to see if the ciruit is still active  			if (mRegionp)  			{	// The simulator will NOT send updates if the object continues normally on the path @@ -2211,9 +2215,12 @@ void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt)  				LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit( mRegionp->getHost() );  				if (cdp)  				{ +					// Find out how many seconds since last packet arrived on the circuit +					F64 time_since_last_packet = LLMessageSystem::getMessageTimeSeconds() - cdp->getLastPacketInTime(); +  					if (!cdp->isAlive() ||		// Circuit is dead or blocked  						 cdp->isBlocked() ||	// or doesn't seem to be getting any packets -						 (mCircuitPacketCount > 0 && mCircuitPacketCount == cdp->getPacketsIn())) +						 (time_since_last_packet > sPhaseOutUpdateInterpolationTime))  					{  						// Start to reduce motion interpolation since we haven't seen a server update in a while  						F64 time_since_last_interpolation = time - mLastInterpUpdateSecs; @@ -2244,9 +2251,6 @@ void LLViewerObject::interpolateLinearMotion(const F64 & time, const F32 & dt)  						new_pos = new_pos * ((F32) phase_out);  						new_v = new_v * ((F32) phase_out);  					} - -					// Save current circuit packet count to see if it changes  -					mCircuitPacketCount = cdp->getPacketsIn();  				}  			}  		} @@ -5100,7 +5104,6 @@ void LLViewerObject::setRegion(LLViewerRegion *regionp)  	}  	mLatestRecvPacketID = 0; -	mCircuitPacketCount = 0;  	mRegionp = regionp;  	for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i) @@ -5400,6 +5403,26 @@ void LLViewerObject::setAttachmentItemID(const LLUUID &id)  	mAttachmentItemID = id;  } +EObjectUpdateType LLViewerObject::getLastUpdateType() const +{ +	return mLastUpdateType; +} + +void LLViewerObject::setLastUpdateType(EObjectUpdateType last_update_type) +{ +	mLastUpdateType = last_update_type; +} + +BOOL LLViewerObject::getLastUpdateCached() const +{ +	return mLastUpdateCached; +} + +void LLViewerObject::setLastUpdateCached(BOOL last_update_cached) +{ +	mLastUpdateCached = last_update_cached; +} +  const LLUUID &LLViewerObject::extractAttachmentItemID()  {  	LLUUID item_id = LLUUID::null; diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index fe670f8827..7afb7f464b 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -77,6 +77,7 @@ typedef enum e_object_update_type  	OUT_TERSE_IMPROVED,  	OUT_FULL_COMPRESSED,  	OUT_FULL_CACHED, +	OUT_UNKNOWN,  } EObjectUpdateType; @@ -226,12 +227,9 @@ public:  	virtual BOOL hasLightTexture() const			{ return FALSE; }  	// This method returns true if the object is over land owned by -	// the agent. -	BOOL isOverAgentOwnedLand() const; - -	// True if over land owned by group of which the agent is -	// either officer or member. -	BOOL isOverGroupOwnedLand() const; +	// the agent, one of its groups, or it encroaches and  +	// anti-encroachment is enabled +	bool isReturnable();  	/*  	// This method will scan through this object, and then query the @@ -615,7 +613,6 @@ protected:  	F64				mLastInterpUpdateSecs;			// Last update for purposes of interpolation  	F64				mLastMessageUpdateSecs;			// Last update from a message from the simulator  	TPACKETID		mLatestRecvPacketID;			// Latest time stamp on message from simulator -	U32				mCircuitPacketCount;			// Packet tracking for early detection of a stopped simulator circuit  	// extra data sent from the sim...currently only used for tree species info  	U8* mData; @@ -696,8 +693,15 @@ public:  	const LLUUID &getAttachmentItemID() const;  	void setAttachmentItemID(const LLUUID &id);  	const LLUUID &extractAttachmentItemID(); // find&set the inventory item ID of the attached object +	EObjectUpdateType getLastUpdateType() const; +	void setLastUpdateType(EObjectUpdateType last_update_type); +	BOOL getLastUpdateCached() const; +	void setLastUpdateCached(BOOL last_update_cached); +  private:  	LLUUID mAttachmentItemID; // ItemID of the associated object is in user inventory. +	EObjectUpdateType	mLastUpdateType; +	BOOL	mLastUpdateCached;  };  /////////////////// diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index f5a32438cf..970cc2e2a7 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -56,6 +56,7 @@  #include "llresmgr.h"  #include "llviewerregion.h"  #include "llviewerstats.h" +#include "llviewerstatsrecorder.h"  #include "llvoavatarself.h"  #include "lltoolmgr.h"  #include "lltoolpie.h" @@ -159,19 +160,13 @@ U64 LLViewerObjectList::getIndex(const U32 local_id,  	return (((U64)index) << 32) | (U64)local_id;  } -BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object) +BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject* objectp)  { -	if(object.getRegion()) +	if(objectp && objectp->getRegion())  	{ -		U32 local_id = object.mLocalID; -		LLHost region_host = object.getRegion()->getHost(); -		if(!region_host.isOk()) -		{ -			return FALSE ; -		} - -		U32 ip = region_host.getAddress(); -		U32 port = region_host.getPort(); +		U32 local_id = objectp->mLocalID;		 +		U32 ip = objectp->getRegion()->getHost().getAddress(); +		U32 port = objectp->getRegion()->getHost().getPort();  		U64 ipport = (((U64)ip) << 32) | (U64)port;  		U32 index = sIPAndPortToIndex[ipport]; @@ -186,7 +181,7 @@ BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)  		}  		// Found existing entry -		if (iter->second == object.getID()) +		if (iter->second == objectp->getID())  		{   // Full UUIDs match, so remove the entry  			sIndexAndLocalIDToUUID.erase(iter);  			return TRUE; @@ -302,8 +297,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  	// have to transform to absolute coordinates.  	num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData); +	// I don't think this case is ever hit.  TODO* Test this.  	if (!cached && !compressed && update_type != OUT_FULL)  	{ +		//llinfos << "TEST: !cached && !compressed && update_type != OUT_FULL" << llendl;  		gTerseObjectUpdates += num_objects;  		S32 size;  		if (mesgsys->getReceiveCompressedSize()) @@ -314,7 +311,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  		{  			size = mesgsys->getReceiveSize();  		} -		// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl; +		//llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;  	}  	else  	{ @@ -345,9 +342,14 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  	U8 compressed_dpbuffer[2048];  	LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);  	LLDataPacker *cached_dpp = NULL; -	 + +#if LL_RECORD_VIEWER_STATS +	LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(regionp); +#endif +  	for (i = 0; i < num_objects; i++)  	{ +		// timer is unused?  		LLTimer update_timer;  		BOOL justCreated = FALSE; @@ -359,9 +361,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);  			// Lookup data packer and add this id to cache miss lists if necessary. -			cached_dpp = regionp->getDP(id, crc); +			U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE; +			cached_dpp = regionp->getDP(id, crc, cache_miss_type);  			if (cached_dpp)  			{ +				// Cache Hit.  				cached_dpp->reset();  				cached_dpp->unpackUUID(fullid, "ID");  				cached_dpp->unpackU32(local_id, "LocalID"); @@ -369,6 +373,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			}  			else  			{ +				// Cache Miss. +				#if LL_RECORD_VIEWER_STATS +				LLViewerStatsRecorder::instance()->recordCacheMissEvent(id, update_type, cache_miss_type); +				#endif +  				continue; // no data packer, skip this object  			}  		} @@ -380,13 +389,15 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			compressed_dp.reset();  			U32 flags = 0; -			if (update_type != OUT_TERSE_IMPROVED) +			if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?  			{  				mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);  			} +			// I don't think we ever use this flag from the server.  DK 2010/12/09  			if (flags & FLAGS_ZLIB_COMPRESSED)  			{ +				//llinfos << "TEST: flags & FLAGS_ZLIB_COMPRESSED" << llendl;  				compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);  				mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);  				uncompressed_length = 2048; @@ -402,7 +413,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			} -			if (update_type != OUT_TERSE_IMPROVED) +			if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?  			{  				compressed_dp.unpackUUID(fullid, "ID");  				compressed_dp.unpackU32(local_id, "LocalID"); @@ -422,7 +433,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				}  			}  		} -		else if (update_type != OUT_FULL) +		else if (update_type != OUT_FULL) // !compressed, !OUT_FULL ==> OUT_FULL_CACHED only?  		{  			mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);  			getUUIDFromLocal(fullid, @@ -435,7 +446,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  				mNumUnknownUpdates++;  			}  		} -		else +		else // OUT_FULL only?  		{  			mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);  			mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); @@ -460,19 +471,19 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			//			<< ", regionp " << (U32) regionp << ", object region " << (U32) objectp->getRegion()  			//			<< llendl;  			//} -			removeFromLocalIDTable(*objectp); +			removeFromLocalIDTable(objectp);  			setUUIDAndLocal(fullid,  							local_id,  							gMessageSystem->getSenderIP(),  							gMessageSystem->getSenderPort());  			if (objectp->mLocalID != local_id) -			{    // Update local ID in object with the one sent from the region +			{	// Update local ID in object with the one sent from the region  				objectp->mLocalID = local_id;  			}  			if (objectp->getRegion() != regionp) -			{    // Object changed region, so update it +			{	// Object changed region, so update it  				objectp->updateRegion(regionp); // for LLVOAvatar  			}  		} @@ -483,18 +494,24 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			{  				if (update_type == OUT_TERSE_IMPROVED)  				{ -					// llinfos << "terse update for an unknown object:" << fullid << llendl; +					// llinfos << "terse update for an unknown object (compressed):" << fullid << llendl; +					#if LL_RECORD_VIEWER_STATS +					LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); +					#endif  					continue;  				}  			} -			else if (cached) +			else if (cached) // Cache hit only?  			{  			}  			else  			{  				if (update_type != OUT_FULL)  				{ -					// llinfos << "terse update for an unknown object:" << fullid << llendl; +					//llinfos << "terse update for an unknown object:" << fullid << llendl; +					#if LL_RECORD_VIEWER_STATS +					LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); +					#endif  					continue;  				} @@ -504,7 +521,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			if (mDeadObjects.find(fullid) != mDeadObjects.end())  			{  				mNumDeadObjectUpdates++; -				// llinfos << "update for a dead object:" << fullid << llendl; +				//llinfos << "update for a dead object:" << fullid << llendl; +				#if LL_RECORD_VIEWER_STATS +				LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); +				#endif  				continue;  			}  #endif @@ -512,6 +532,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());  			if (!objectp)  			{ +				llinfos << "createObject failure for object: " << fullid << llendl; +				#if LL_RECORD_VIEWER_STATS +				LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); +				#endif  				continue;  			}  			justCreated = TRUE; @@ -524,19 +548,26 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;  		} +		bool bCached = false;  		if (compressed)  		{ -			if (update_type != OUT_TERSE_IMPROVED) +			if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?  			{  				objectp->mLocalID = local_id;  			}  			processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated); -			if (update_type != OUT_TERSE_IMPROVED) +			if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only?  			{ +				bCached = true; +				#if LL_RECORD_VIEWER_STATS +				LLViewerRegion::eCacheUpdateResult result = objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp); +				LLViewerStatsRecorder::instance()->recordCacheFullUpdate(local_id, update_type, result, objectp); +				#else  				objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp); +				#endif  			}  		} -		else if (cached) +		else if (cached) // Cache hit only?  		{  			objectp->mLocalID = local_id;  			processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated); @@ -549,8 +580,17 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,  			}  			processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);  		} +		#if LL_RECORD_VIEWER_STATS +		LLViewerStatsRecorder::instance()->recordObjectUpdateEvent(local_id, update_type, objectp); +		#endif +		objectp->setLastUpdateType(update_type); +		objectp->setLastUpdateCached(bCached);  	} +#if LL_RECORD_VIEWER_STATS +	LLViewerStatsRecorder::instance()->endObjectUpdateEvents(); +#endif +  	LLVOAvatar::cullAvatarsByPixelArea();  } @@ -681,12 +721,12 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)  	// update global timer  	F32 last_time = gFrameTimeSeconds; -	U64 time = totalTime();                 // this will become the new gFrameTime when the update is done +	U64 time = totalTime();				 // this will become the new gFrameTime when the update is done  	// Time _can_ go backwards, for example if the user changes the system clock.  	// It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.  //	llassert(time > gFrameTime);  	F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC; -	gFrameTime    = time; +	gFrameTime	= time;  	F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;  	gFrameTimeSeconds = (F32)time_since_start; @@ -788,7 +828,7 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)  		{  			std::string id_str;  			objectp->mID.toString(id_str); -			std::string tmpstr = std::string("Par:    ") + id_str; +			std::string tmpstr = std::string("Par:	") + id_str;  			addDebugBeacon(objectp->getPositionAgent(),  							tmpstr,  							LLColor4(1.f,0.f,0.f,1.f), @@ -808,12 +848,12 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)  			std::string tmpstr;  			if (objectp->getParent())  			{ -				tmpstr = std::string("ChP:    ") + id_str; +				tmpstr = std::string("ChP:	") + id_str;  				text_color = LLColor4(0.f, 1.f, 0.f, 1.f);  			}  			else  			{ -				tmpstr = std::string("ChNoP:    ") + id_str; +				tmpstr = std::string("ChNoP:	") + id_str;  				text_color = LLColor4(1.f, 0.f, 0.f, 1.f);  			}  			id = sIndexAndLocalIDToUUID[oi.mParentInfo]; @@ -864,7 +904,7 @@ void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)  	//				<< objectp->getRegion()->getHost().getPort() << llendl;  	//}	 -	removeFromLocalIDTable(*objectp); +	removeFromLocalIDTable(objectp);  	if (objectp->onActiveList())  	{ @@ -1519,8 +1559,8 @@ void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)  			llinfos << "Agent: " << objectp->getPositionAgent() << llendl;  			addDebugBeacon(objectp->getPositionAgent(),"");  #endif -            gPipeline.markMoved(objectp->mDrawable);                 -            objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE); +			gPipeline.markMoved(objectp->mDrawable);				 +			objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);  			// Flag the object as no longer orphaned  			childp->mOrphaned = FALSE; diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 605bac8e89..fda3d6899d 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -160,7 +160,7 @@ public:  								const U32 ip,  								const U32 port); // Requires knowledge of message system info! -	static BOOL removeFromLocalIDTable(const LLViewerObject &object); +	static BOOL removeFromLocalIDTable(const LLViewerObject* objectp);  	// Used ONLY by the orphaned object code.  	static U64 getIndex(const U32 local_id, const U32 ip, const U32 port); diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp index eee653b0c1..d07e06f6a7 100644 --- a/indra/newview/llviewerparceloverlay.cpp +++ b/indra/newview/llviewerparceloverlay.cpp @@ -145,6 +145,35 @@ BOOL LLViewerParcelOverlay::isOwnedOther(const LLVector3& pos) const  	return (PARCEL_OWNED == overlay || PARCEL_FOR_SALE == overlay);  } +bool LLViewerParcelOverlay::encroachesOwned(const std::vector<LLBBox>& boxes) const +{ +	// boxes are expected to already be axis aligned +	for (U32 i = 0; i < boxes.size(); ++i) +	{ +		LLVector3 min = boxes[i].getMinAgent(); +		LLVector3 max = boxes[i].getMaxAgent(); +		 +		S32 left   = S32(llclamp((min.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +		S32 right  = S32(llclamp((max.mV[VX] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +		S32 top    = S32(llclamp((min.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +		S32 bottom = S32(llclamp((max.mV[VY] / PARCEL_GRID_STEP_METERS), 0.f, REGION_WIDTH_METERS - 1)); +	 +		for (S32 row = top; row <= bottom; row++) +		{ +			for (S32 column = left; column <= right; column++) +			{ +				U8 type = ownership(row, column); +				if ((PARCEL_SELF == type) +					|| (PARCEL_GROUP == type)) +				{ +					return true; +				} +			} +		} +	} +	return false; +} +  BOOL LLViewerParcelOverlay::isSoundLocal(const LLVector3& pos) const  {  	S32 row =    S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS); diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h index 61be220312..c80baedda6 100644 --- a/indra/newview/llviewerparceloverlay.h +++ b/indra/newview/llviewerparceloverlay.h @@ -30,6 +30,7 @@  // The ownership data for land parcels.  // One of these structures per region. +#include "llbbox.h"  #include "lldarray.h"  #include "llframetimer.h"  #include "lluuid.h" @@ -54,6 +55,12 @@ public:  	BOOL			isOwnedSelf(const LLVector3& pos) const;  	BOOL			isOwnedGroup(const LLVector3& pos) const;  	BOOL			isOwnedOther(const LLVector3& pos) const; + +	// "encroaches" means the prim hangs over the parcel, but its center +	// might be in another parcel. for now, we simply test axis aligned  +	// bounding boxes which isn't perfect, but is close +	bool encroachesOwned(const std::vector<LLBBox>& boxes) const; +	  	BOOL			isSoundLocal(const LLVector3& pos) const;  	BOOL			isBuildCameraAllowed(const LLVector3& pos) const; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 551ba18dd5..23b7b921b8 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -59,6 +59,7 @@  #include "llurldispatcher.h"  #include "llviewerobjectlist.h"  #include "llviewerparceloverlay.h" +#include "llviewerstatsrecorder.h"  #include "llvlmanager.h"  #include "llvlcomposition.h"  #include "llvocache.h" @@ -321,6 +322,12 @@ LLViewerRegion::~LLViewerRegion()  	std::for_each(mObjectPartition.begin(), mObjectPartition.end(), DeletePointer());  } +/*virtual*/  +const LLHost&	LLViewerRegion::getHost() const				 +{  +	return mHost;  +} +  void LLViewerRegion::loadObjectCache()  {  	if (mCacheLoaded) @@ -1032,7 +1039,7 @@ void LLViewerRegion::getInfo(LLSD& info)  	info["Region"]["Handle"]["y"] = (LLSD::Integer)y;  } -void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp) +LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)  {  	U32 local_id = objectp->getLocalID();  	U32 crc = objectp->getCRC(); @@ -1046,35 +1053,36 @@ void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinary  		{  			// Record a hit  			entry->recordDupe(); +			return CACHE_UPDATE_DUPE;  		} -		else -		{ -			// Update the cache entry -			mCacheMap.erase(local_id); -			delete entry; -			entry = new LLVOCacheEntry(local_id, crc, dp); -			mCacheMap[local_id] = entry; -		} -	} -	else -	{ -		// we haven't seen this object before -		// Create new entry and add to map -		if (mCacheMap.size() > MAX_OBJECT_CACHE_ENTRIES) -		{ -			mCacheMap.erase(mCacheMap.begin()); -		} +		// Update the cache entry +		mCacheMap.erase(local_id); +		delete entry;  		entry = new LLVOCacheEntry(local_id, crc, dp); -  		mCacheMap[local_id] = entry; +		return CACHE_UPDATE_CHANGED;  	} -	return ; + +	// we haven't seen this object before + +	// Create new entry and add to map +	eCacheUpdateResult result = CACHE_UPDATE_ADDED; +	if (mCacheMap.size() > MAX_OBJECT_CACHE_ENTRIES) +	{ +		mCacheMap.erase(mCacheMap.begin()); +		result = CACHE_UPDATE_REPLACED; +		 +	} +	entry = new LLVOCacheEntry(local_id, crc, dp); + +	mCacheMap[local_id] = entry; +	return result;  }  // Get data packer for this object, if we have cached data  // AND the CRC matches. JC -LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc) +LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc, U8 &cache_miss_type)  {  	llassert(mCacheLoaded); @@ -1087,17 +1095,20 @@ LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc)  		{  			// Record a hit  			entry->recordHit(); +			cache_miss_type = CACHE_MISS_TYPE_NONE;  			return entry->getDP(crc);  		}  		else  		{  			// llinfos << "CRC miss for " << local_id << llendl; +			cache_miss_type = CACHE_MISS_TYPE_CRC;  			mCacheMissCRC.put(local_id);  		}  	}  	else  	{  		// llinfos << "Cache miss for " << local_id << llendl; +		cache_miss_type = CACHE_MISS_TYPE_FULL;  		mCacheMissFull.put(local_id);  	}  	return NULL; @@ -1119,9 +1130,6 @@ void LLViewerRegion::requestCacheMisses()  	S32 blocks = 0;  	S32 i; -	const U8 CACHE_MISS_TYPE_FULL = 0; -	const U8 CACHE_MISS_TYPE_CRC  = 1; -  	// Send full cache miss updates.  For these, we KNOW we don't  	// have a viewer object.  	for (i = 0; i < full_count; i++) @@ -1184,6 +1192,11 @@ void LLViewerRegion::requestCacheMisses()  	mCacheDirty = TRUE ;  	// llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl; +	#if LL_RECORD_VIEWER_STATS +	LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(this); +	LLViewerStatsRecorder::instance()->recordRequestCacheMissesEvent(full_count + crc_count); +	LLViewerStatsRecorder::instance()->endObjectUpdateEvents(); +	#endif  }  void LLViewerRegion::dumpCache() @@ -1372,11 +1385,12 @@ void LLViewerRegion::setSeedCapability(const std::string& url)  	capabilityNames.append("DispatchRegionInfo");  	capabilityNames.append("EstateChangeInfo");  	capabilityNames.append("EventQueueGet"); -	capabilityNames.append("FetchInventory");  	capabilityNames.append("ObjectMedia");  	capabilityNames.append("ObjectMediaNavigate"); -	capabilityNames.append("FetchLib"); -	capabilityNames.append("FetchLibDescendents"); +	capabilityNames.append("FetchLib2"); +	capabilityNames.append("FetchLibDescendents2"); +	capabilityNames.append("FetchInventory2"); +	capabilityNames.append("FetchInventoryDescendents2");  	capabilityNames.append("GetDisplayNames");  	capabilityNames.append("GetTexture");  	capabilityNames.append("GroupProposalBallot"); @@ -1400,7 +1414,6 @@ void LLViewerRegion::setSeedCapability(const std::string& url)  	capabilityNames.append("SendUserReportWithScreenshot");  	capabilityNames.append("ServerReleaseNotes");  	capabilityNames.append("SetDisplayName"); -	capabilityNames.append("SimConsole");  	capabilityNames.append("SimConsoleAsync");  	capabilityNames.append("StartGroupProposal");  	capabilityNames.append("TextureStats"); @@ -1417,7 +1430,6 @@ void LLViewerRegion::setSeedCapability(const std::string& url)  	capabilityNames.append("ViewerMetrics");  	capabilityNames.append("ViewerStartAuction");  	capabilityNames.append("ViewerStats"); -	capabilityNames.append("WebFetchInventoryDescendents");  	// Please add new capabilities alphabetically to reduce  	// merge conflicts. @@ -1497,6 +1509,20 @@ LLSpatialPartition* LLViewerRegion::getSpatialPartition(U32 type)  	return NULL;  } +// the viewer can not yet distinquish between normal- and estate-owned objects +// so we collapse these two bits and enable the UI if either are set +const U32 ALLOW_RETURN_ENCROACHING_OBJECT = REGION_FLAGS_ALLOW_RETURN_ENCROACHING_OBJECT +											| REGION_FLAGS_ALLOW_RETURN_ENCROACHING_ESTATE_OBJECT; + +bool LLViewerRegion::objectIsReturnable(const LLVector3& pos, const std::vector<LLBBox>& boxes) const +{ +	return (mParcelOverlay != NULL) +		&& (mParcelOverlay->isOwnedSelf(pos) +			|| mParcelOverlay->isOwnedGroup(pos) +			|| ((mRegionFlags & ALLOW_RETURN_ENCROACHING_OBJECT) +				&& mParcelOverlay->encroachesOwned(boxes)) ); +} +  void LLViewerRegion::showReleaseNotes()  {  	std::string url = this->getCapability("ServerReleaseNotes"); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 8b71998f60..dd40b876cd 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -33,6 +33,7 @@  #include "lldarray.h"  #include "llwind.h" +#include "llbbox.h"  #include "llcloud.h"  #include "llstat.h"  #include "v3dmath.h" @@ -50,7 +51,7 @@  // Surface id's  #define LAND  1  #define WATER 2 -const U32	MAX_OBJECT_CACHE_ENTRIES = 10000; +const U32	MAX_OBJECT_CACHE_ENTRIES = 50000;  class LLEventPoll; @@ -244,7 +245,7 @@ public:      LLEventPump& getCapAPI() { return mCapabilityListener.getCapAPI(); }      /// implements LLCapabilityProvider -	virtual LLHost	getHost() const				{ return mHost; } +	/*virtual*/ const LLHost& getHost() const;  	const U64 		&getHandle() const 			{ return mHandle; }  	LLSurface		&getLand() const			{ return *mLandp; } @@ -274,9 +275,24 @@ public:  	void getInfo(LLSD& info); +	typedef enum +	{ +		CACHE_MISS_TYPE_FULL = 0, +		CACHE_MISS_TYPE_CRC, +		CACHE_MISS_TYPE_NONE +	} eCacheMissType; + +	typedef enum +	{ +		CACHE_UPDATE_DUPE = 0, +		CACHE_UPDATE_CHANGED, +		CACHE_UPDATE_ADDED, +		CACHE_UPDATE_REPLACED +	} eCacheUpdateResult; +  	// handle a full update message -	void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp); -	LLDataPacker *getDP(U32 local_id, U32 crc); +	eCacheUpdateResult cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp); +	LLDataPacker *getDP(U32 local_id, U32 crc, U8 &cache_miss_type);  	void requestCacheMisses();  	void addCacheMissFull(const U32 local_id); @@ -293,6 +309,8 @@ public:  	std::string getHttpUrl() const { return mHttpUrl ;}  	LLSpatialPartition* getSpatialPartition(U32 type); + +	bool objectIsReturnable(const LLVector3& pos, const std::vector<LLBBox>& boxes) const;  public:  	struct CompareDistance  	{ diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp new file mode 100644 index 0000000000..e9d21b4848 --- /dev/null +++ b/indra/newview/llviewerstatsrecorder.cpp @@ -0,0 +1,258 @@ +/** + * @file llviewerstatsrecorder.cpp + * @brief record info about viewer events to a metrics log file + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewerstatsrecorder.h" + +#if LL_RECORD_VIEWER_STATS + +#include "llfile.h" +#include "llviewerregion.h" +#include "llviewerobject.h" + + +// To do - something using region name or global position +#if LL_WINDOWS +	static const std::string STATS_FILE_NAME("C:\\ViewerObjectCacheStats.csv"); +#else +	static const std::string STATS_FILE_NAME("/tmp/viewerstats.csv"); +#endif + +LLViewerStatsRecorder* LLViewerStatsRecorder::sInstance = NULL; +LLViewerStatsRecorder::LLViewerStatsRecorder() : +	mObjectCacheFile(NULL), +	mTimer(), +	mRegionp(NULL), +	mStartTime(0.f), +	mProcessingTime(0.f) +{ +	if (NULL != sInstance) +	{ +		llerrs << "Attempted to create multiple instances of LLViewerStatsRecorder!" << llendl; +	} +	sInstance = this; +	clearStats(); +} + +LLViewerStatsRecorder::~LLViewerStatsRecorder() +{ +	if (mObjectCacheFile != NULL) +	{ +		LLFile::close(mObjectCacheFile); +		mObjectCacheFile = NULL; +	} +} + +// static +void LLViewerStatsRecorder::initClass() +{ +	sInstance = new LLViewerStatsRecorder(); +} + +// static +void LLViewerStatsRecorder::cleanupClass() +{ +	delete sInstance; +	sInstance = NULL; +} + + +void LLViewerStatsRecorder::initStatsRecorder(LLViewerRegion *regionp) +{ +	if (mObjectCacheFile == NULL) +	{ +		mStartTime = LLTimer::getTotalTime(); +		mObjectCacheFile = LLFile::fopen(STATS_FILE_NAME, "wb"); +		if (mObjectCacheFile) +		{	// Write column headers +			std::ostringstream data_msg; +			data_msg << "EventTime, " +				<< "ProcessingTime, " +				<< "CacheHits, " +				<< "CacheFullMisses, " +				<< "CacheCrcMisses, " +				<< "FullUpdates, " +				<< "TerseUpdates, " +				<< "CacheMissRequests, " +				<< "CacheMissResponses, " +				<< "CacheUpdateDupes, " +				<< "CacheUpdateChanges, " +				<< "CacheUpdateAdds, " +				<< "CacheUpdateReplacements, " +				<< "UpdateFailures" +				<< "\n"; + +			fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile ); +		} +	} +} + +void LLViewerStatsRecorder::beginObjectUpdateEvents(LLViewerRegion *regionp) +{ +	initStatsRecorder(regionp); +	mRegionp = regionp; +	mProcessingTime = LLTimer::getTotalTime(); +	clearStats(); +} + +void LLViewerStatsRecorder::clearStats() +{ +	mObjectCacheHitCount = 0; +	mObjectCacheMissFullCount = 0; +	mObjectCacheMissCrcCount = 0; +	mObjectFullUpdates = 0; +	mObjectTerseUpdates = 0; +	mObjectCacheMissRequests = 0; +	mObjectCacheMissResponses = 0; +	mObjectCacheUpdateDupes = 0; +	mObjectCacheUpdateChanges = 0; +	mObjectCacheUpdateAdds = 0; +	mObjectCacheUpdateReplacements = 0; +	mObjectUpdateFailures = 0; +} + + +void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type) +{ +	mObjectUpdateFailures++; +} + +void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type) +{ +	if (LLViewerRegion::CACHE_MISS_TYPE_FULL == cache_miss_type) +	{ +		mObjectCacheMissFullCount++; +	} +	else +	{ +		mObjectCacheMissCrcCount++; +	} +} + +void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp) +{ +	switch (update_type) +	{ +	case OUT_FULL: +		mObjectFullUpdates++; +		break; +	case OUT_TERSE_IMPROVED: +		mObjectTerseUpdates++; +		break; +	case OUT_FULL_COMPRESSED: +		mObjectCacheMissResponses++; +		break; +	case OUT_FULL_CACHED: +		mObjectCacheHitCount++; +		break; +	default: +		llwarns << "Unknown update_type" << llendl; +		break; +	}; +} + +void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp) +{ +	switch (update_result) +	{ +		case LLViewerRegion::CACHE_UPDATE_DUPE: +			mObjectCacheUpdateDupes++; +			break; +		case LLViewerRegion::CACHE_UPDATE_CHANGED: +			mObjectCacheUpdateChanges++; +			break; +		case LLViewerRegion::CACHE_UPDATE_ADDED: +			mObjectCacheUpdateAdds++; +			break; +		case LLViewerRegion::CACHE_UPDATE_REPLACED: +			mObjectCacheUpdateReplacements++; +			break; +		default: +			llwarns << "Unknown update_result type" << llendl; +			break; +	}; +} + +void LLViewerStatsRecorder::recordRequestCacheMissesEvent(S32 count) +{ +	mObjectCacheMissRequests += count; +} + +void LLViewerStatsRecorder::endObjectUpdateEvents() +{ +	llinfos << "ILX: "  +		<< mObjectCacheHitCount << " hits, "  +		<< mObjectCacheMissFullCount << " full misses, " +		<< mObjectCacheMissCrcCount << " crc misses, " +		<< mObjectFullUpdates << " full updates, " +		<< mObjectTerseUpdates << " terse updates, " +		<< mObjectCacheMissRequests << " cache miss requests, " +		<< mObjectCacheMissResponses << " cache miss responses, " +		<< mObjectCacheUpdateDupes << " cache update dupes, " +		<< mObjectCacheUpdateChanges << " cache update changes, " +		<< mObjectCacheUpdateAdds << " cache update adds, " +		<< mObjectCacheUpdateReplacements << " cache update replacements, " +		<< mObjectUpdateFailures << " update failures" +		<< llendl; + +	S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; +	if (mObjectCacheFile != NULL && +		total_objects > 0) +	{ +		std::ostringstream data_msg; +		F32 processing32 = (F32) ((LLTimer::getTotalTime() - mProcessingTime) / 1000.0); + +		data_msg << getTimeSinceStart() +			<< ", " << processing32 +			<< ", " << mObjectCacheHitCount +			<< ", " << mObjectCacheMissFullCount +			<< ", " << mObjectCacheMissCrcCount +			<< ", " << mObjectFullUpdates +			<< ", " << mObjectTerseUpdates +			<< ", " << mObjectCacheMissRequests +			<< ", " << mObjectCacheMissResponses +			<< ", " << mObjectCacheUpdateDupes +			<< ", " << mObjectCacheUpdateChanges +			<< ", " << mObjectCacheUpdateAdds +			<< ", " << mObjectCacheUpdateReplacements +			<< ", " << mObjectUpdateFailures +			<< "\n"; + +		fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile ); +	} + +	clearStats(); +} + +F32 LLViewerStatsRecorder::getTimeSinceStart() +{ +	return (F32) ((LLTimer::getTotalTime() - mStartTime) / 1000.0); +} + +#endif + + + diff --git a/indra/newview/llviewerstatsrecorder.h b/indra/newview/llviewerstatsrecorder.h new file mode 100644 index 0000000000..612ac380f7 --- /dev/null +++ b/indra/newview/llviewerstatsrecorder.h @@ -0,0 +1,97 @@ +/** + * @file llviewerstatsrecorder.h + * @brief record info about viewer events to a metrics log file + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LLVIEWERSTATSRECORDER_H +#define LLVIEWERSTATSRECORDER_H + + +// This is a diagnostic class used to record information from the viewer +// for analysis. + +// This is normally 0.  Set to 1 to enable viewer stats recording +#define LL_RECORD_VIEWER_STATS	0 + + +#if LL_RECORD_VIEWER_STATS +#include "llframetimer.h" +#include "llviewerobject.h" +#include "llviewerregion.h" + +class LLMutex; +class LLViewerRegion; +class LLViewerObject; + +class LLViewerStatsRecorder +{ + public: +	LLViewerStatsRecorder(); +	~LLViewerStatsRecorder(); + +	static void initClass(); +	static void cleanupClass(); +	static LLViewerStatsRecorder* instance() {return sInstance; } + +	void initStatsRecorder(LLViewerRegion *regionp); + +	void beginObjectUpdateEvents(LLViewerRegion *regionp); +	void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type); +	void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type); +	void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp); +	void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp); +	void recordRequestCacheMissesEvent(S32 count); +	void endObjectUpdateEvents(); + +	F32 getTimeSinceStart(); + +private: +	static LLViewerStatsRecorder* sInstance; + +	LLFILE *	mObjectCacheFile;		// File to write data into +	LLFrameTimer	mTimer; +	LLViewerRegion*	mRegionp; +	F64			mStartTime; +	F64			mProcessingTime; + +	S32			mObjectCacheHitCount; +	S32			mObjectCacheMissFullCount; +	S32			mObjectCacheMissCrcCount; +	S32			mObjectFullUpdates; +	S32			mObjectTerseUpdates; +	S32			mObjectCacheMissRequests; +	S32			mObjectCacheMissResponses; +	S32			mObjectCacheUpdateDupes; +	S32			mObjectCacheUpdateChanges; +	S32			mObjectCacheUpdateAdds; +	S32			mObjectCacheUpdateReplacements; +	S32			mObjectUpdateFailures; + + +	void	clearStats(); +}; +#endif	// LL_RECORD_VIEWER_STATS + +#endif // LLVIEWERSTATSRECORDER_H + diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 0c05a301e6..cd16b15e3e 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1214,12 +1214,15 @@ void LLViewerFetchedTexture::cleanup()  void LLViewerFetchedTexture::setForSculpt()  { +	static const S32 MAX_INTERVAL = 8 ; //frames +  	mForSculpt = TRUE ;  	if(isForSculptOnly() && !getBoundRecently())  	{  		destroyGLTexture() ; //sculpt image does not need gl texture.  	}  	checkCachedRawSculptImage() ; +	setMaxVirtualSizeResetInterval(MAX_INTERVAL) ;  }  BOOL LLViewerFetchedTexture::isForSculptOnly() const diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c812fcf2da..166b110412 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -296,13 +296,15 @@ private:  	line_list_t mLineList;  	LLColor4 mTextColor; -public: -	LLDebugText(LLViewerWindow* window) : mWindow(window) {} -	  	void addText(S32 x, S32 y, const std::string &text)   	{  		mLineList.push_back(Line(text, x, y));  	} +	 +	void clearText() { mLineList.clear(); } +	 +public: +	LLDebugText(LLViewerWindow* window) : mWindow(window) {}  	void update()  	{ @@ -323,6 +325,8 @@ public:  		U32 ypos = 64;  		const U32 y_inc = 20; +		clearText(); +		  		if (gSavedSettings.getBOOL("DebugShowTime"))  		{  			const U32 y_inc2 = 15; @@ -601,6 +605,50 @@ public:  				ypos += y_inc;  			}  		} +		 +		if (gSavedSettings.getBOOL("DebugShowTextureInfo")) +		{ +			LLViewerObject* objectp = NULL ; +			//objectp = = gAgentCamera.getFocusObject(); +			 +			LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode(); +			if (nodep) +			{ +				objectp = nodep->getObject();			 +			} +			if (objectp && !objectp->isDead()) +			{ +				S32 num_faces = objectp->mDrawable->getNumFaces() ; +				 +				for(S32 i = 0 ; i < num_faces; i++) +				{ +					LLFace* facep = objectp->mDrawable->getFace(i) ; +					if(facep) +					{ +						//addText(xpos, ypos, llformat("ts_min: %.3f ts_max: %.3f tt_min: %.3f tt_max: %.3f", facep->mTexExtents[0].mV[0], facep->mTexExtents[1].mV[0], +						//		facep->mTexExtents[0].mV[1], facep->mTexExtents[1].mV[1])); +						//ypos += y_inc; +						 +						addText(xpos, ypos, llformat("v_size: %.3f:  p_size: %.3f", facep->getVirtualSize(), facep->getPixelArea())); +						ypos += y_inc; +						 +						//const LLTextureEntry *tep = facep->getTextureEntry(); +						//if(tep) +						//{ +						//	addText(xpos, ypos, llformat("scale_s: %.3f:  scale_t: %.3f", tep->mScaleS, tep->mScaleT)) ; +						//	ypos += y_inc; +						//} +						 +						LLViewerTexture* tex = facep->getTexture() ; +						if(tex) +						{ +							addText(xpos, ypos, llformat("ID: %s v_size: %.3f", tex->getID().asString().c_str(), tex->getMaxVirtualSize())); +							ypos += y_inc; +						} +					} +				} +			} +		}  	}  	void draw() diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index bb4c5b1804..fd89044995 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -2106,31 +2106,6 @@ void LLVOAvatar::computeBodySize()  			gAgent.sendAgentSetAppearance();  		}  	} - -/* debug spam -	std::cout << "skull = " << skull << std::endl;				// adebug -	std::cout << "head = " << head << std::endl;				// adebug -	std::cout << "head_scale = " << head_scale << std::endl;	// adebug -	std::cout << "neck = " << neck << std::endl;				// adebug -	std::cout << "neck_scale = " << neck_scale << std::endl;	// adebug -	std::cout << "chest = " << chest << std::endl;				// adebug -	std::cout << "chest_scale = " << chest_scale << std::endl;	// adebug -	std::cout << "torso = " << torso << std::endl;				// adebug -	std::cout << "torso_scale = " << torso_scale << std::endl;	// adebug -	std::cout << std::endl;	// adebug - -	std::cout << "pelvis_scale = " << pelvis_scale << std::endl;// adebug -	std::cout << std::endl;	// adebug - -	std::cout << "hip = " << hip << std::endl;					// adebug -	std::cout << "hip_scale = " << hip_scale << std::endl;		// adebug -	std::cout << "ankle = " << ankle << std::endl;				// adebug -	std::cout << "ankle_scale = " << ankle_scale << std::endl;	// adebug -	std::cout << "foot = " << foot << std::endl;				// adebug -	std::cout << "mBodySize = " << mBodySize << std::endl;		// adebug -	std::cout << "mPelvisToFoot = " << mPelvisToFoot << std::endl;	// adebug -	std::cout << std::endl;		// adebug -*/  }  //------------------------------------------------------------------------ diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index 145ee31260..b3312db4a0 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -40,6 +40,7 @@ BOOL check_write(LLAPRFile* apr_file, void* src, S32 n_bytes)  	return apr_file->write(src, n_bytes) == n_bytes ;  } +  //---------------------------------------------------------------------------  // LLVOCacheEntry  //--------------------------------------------------------------------------- @@ -212,8 +213,8 @@ BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const  		if(success)  		{  			success = check_write(apr_file, (void*)mBuffer, size); +		}  	} -}  	return success ;  } @@ -224,7 +225,9 @@ BOOL LLVOCacheEntry::writeToFile(LLAPRFile* apr_file) const  // Format string used to construct filename for the object cache  static const char OBJECT_CACHE_FILENAME[] = "objects_%d_%d.slc"; -const U32 NUM_ENTRIES_TO_PURGE = 16 ; +const U32 MAX_NUM_OBJECT_ENTRIES = 128 ; +const U32 MIN_ENTRIES_TO_PURGE = 16 ; +const U32 INVALID_TIME = 0 ;  const char* object_cache_dirname = "objectcache";  const char* header_filename = "object.cache"; @@ -286,22 +289,27 @@ void LLVOCache::setDirNames(ELLPath location)  void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version)  { -	if(mInitialized || !mEnabled) +	if(!mEnabled)  	{ +		llwarns << "Not initializing cache: Cache is currently disabled." << llendl;  		return ;  	} +	if(mInitialized) +	{ +		llwarns << "Cache already initialized." << llendl; +		return ; +	} +	mInitialized = TRUE ; +  	setDirNames(location);  	if (!mReadOnly)  	{  		LLFile::mkdir(mObjectCacheDirName);  	} - -	mCacheSize = size; - +	mCacheSize = llclamp(size, MIN_ENTRIES_TO_PURGE, MAX_NUM_OBJECT_ENTRIES);  	mMetaInfo.mVersion = cache_version; -	readCacheHeader(); -	mInitialized = TRUE ; +	readCacheHeader();	  	if(mMetaInfo.mVersion != cache_version)   	{ @@ -321,12 +329,16 @@ void LLVOCache::removeCache(ELLPath location)  {  	if(mReadOnly)  	{ +		llwarns << "Not removing cache at " << location << ": Cache is currently in read-only mode." << llendl;  		return ;  	} +	llinfos << "about to remove the object cache due to settings." << llendl ; +  	std::string delem = gDirUtilp->getDirDelimiter();  	std::string mask = delem + "*";  	std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname); +	llinfos << "Removing cache at " << cache_dir << llendl;  	gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files  	LLFile::rmdir(cache_dir); @@ -339,17 +351,56 @@ void LLVOCache::removeCache()  	llassert_always(mInitialized) ;  	if(mReadOnly)  	{ +		llwarns << "Not clearing object cache: Cache is currently in read-only mode." << llendl;  		return ;  	} +	llinfos << "about to remove the object cache due to some error." << llendl ; +  	std::string delem = gDirUtilp->getDirDelimiter();  	std::string mask = delem + "*"; +	llinfos << "Removing cache at " << mObjectCacheDirName << llendl;  	gDirUtilp->deleteFilesInDir(mObjectCacheDirName, mask);   	clearCacheInMemory() ;  	writeCacheHeader();  } +void LLVOCache::removeEntry(HeaderEntryInfo* entry)  +{ +	llassert_always(mInitialized) ; +	if(mReadOnly) +	{ +		return ; +	} +	if(!entry) +	{ +		return ; +	} + +	header_entry_queue_t::iterator iter = mHeaderEntryQueue.find(entry) ; +	if(iter != mHeaderEntryQueue.end()) +	{		 +		mHandleEntryMap.erase(entry->mHandle) ;		 +		mHeaderEntryQueue.erase(iter) ; +		removeFromCache(entry) ; +		delete entry ; + +		mNumEntries = mHandleEntryMap.size() ; +	} +} + +void LLVOCache::removeEntry(U64 handle)  +{ +	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ; +	if(iter == mHandleEntryMap.end()) //no cache +	{ +		return ; +	} +	HeaderEntryInfo* entry = iter->second ; +	removeEntry(entry) ; +} +  void LLVOCache::clearCacheInMemory()  {  	if(!mHeaderEntryQueue.empty())  @@ -362,6 +413,7 @@ void LLVOCache::clearCacheInMemory()  		mHandleEntryMap.clear();  		mNumEntries = 0 ;  	} +  }  void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)  @@ -375,146 +427,169 @@ void LLVOCache::getObjectCacheFilename(U64 handle, std::string& filename)  	return ;  } -void LLVOCache::removeFromCache(U64 handle) +void LLVOCache::removeFromCache(HeaderEntryInfo* entry)  {  	if(mReadOnly)  	{ +		llwarns << "Not removing cache for handle " << entry->mHandle << ": Cache is currently in read-only mode." << llendl;  		return ;  	}  	std::string filename; -	getObjectCacheFilename(handle, filename); -	LLAPRFile::remove(filename, mLocalAPRFilePoolp);	 -} - -BOOL LLVOCache::checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes)  -{ -	if(!check_read(apr_file, src, n_bytes)) -	{ -		delete apr_file ; -		removeCache() ; -		return FALSE ; -	} - -	return TRUE ; -} - -BOOL LLVOCache::checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes)  -{ -	if(!check_write(apr_file, src, n_bytes)) -	{ -		delete apr_file ; -		removeCache() ; -		return FALSE ; -	} - -	return TRUE ; +	getObjectCacheFilename(entry->mHandle, filename); +	LLAPRFile::remove(filename, mLocalAPRFilePoolp); +	entry->mTime = INVALID_TIME ; +	updateEntry(entry) ; //update the head file.  }  void LLVOCache::readCacheHeader()  {  	if(!mEnabled)  	{ -		return ; +		llwarns << "Not reading cache header: Cache is currently disabled." << llendl; +		return;  	}  	//clear stale info.  	clearCacheInMemory();	 +	bool success = true ;  	if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))  	{ -		LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);		 +		LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);		  		//read the meta element -		if(!checkRead(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo))) -		{ -			return ; -		} - -		HeaderEntryInfo* entry ; -		mNumEntries = 0 ; -		while(mNumEntries < mCacheSize) +		success = check_read(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ; +		 +		if(success)  		{ -			entry = new HeaderEntryInfo() ; -			if(!checkRead(apr_file, entry, sizeof(HeaderEntryInfo))) +			HeaderEntryInfo* entry = NULL ; +			mNumEntries = 0 ; +			U32 num_read = 0 ; +			while(num_read++ < MAX_NUM_OBJECT_ENTRIES)  			{ -				delete entry ;			 -				return ; +				if(!entry) +				{ +					entry = new HeaderEntryInfo() ; +				} +				success = check_read(&apr_file, entry, sizeof(HeaderEntryInfo)); +								 +				if(!success) //failed +				{ +					llwarns << "Error reading cache header entry. (entry_index=" << mNumEntries << ")" << llendl; +					delete entry ; +					entry = NULL ; +					break ; +				} +				else if(entry->mTime == INVALID_TIME) +				{ +					continue ; //an empty entry +				} + +				entry->mIndex = mNumEntries++ ; +				mHeaderEntryQueue.insert(entry) ; +				mHandleEntryMap[entry->mHandle] = entry ; +				entry = NULL ;  			} -			else if(!entry->mTime) //end of the cache. +			if(entry)  			{  				delete entry ; -				return ;  			} - -			entry->mIndex = mNumEntries++ ; -			mHeaderEntryQueue.insert(entry) ; -			mHandleEntryMap[entry->mHandle] = entry ;  		} -		delete apr_file ; +		//--------- +		//debug code +		//---------- +		//std::string name ; +		//for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter) +		//{ +		//	getObjectCacheFilename((*iter)->mHandle, name) ; +		//	llinfos << name << llendl ; +		//} +		//-----------  	}  	else  	{  		writeCacheHeader() ;  	} + +	if(!success) +	{ +		removeCache() ; //failed to read header, clear the cache +	} +	else if(mNumEntries >= mCacheSize) +	{ +		purgeEntries(mCacheSize) ; +	} + +	return ;  }  void LLVOCache::writeCacheHeader()  { -	if(mReadOnly || !mEnabled) +	if (!mEnabled)  	{ -		return ; -	}	 - -	LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); +		llwarns << "Not writing cache header: Cache is currently disabled." << llendl; +		return; +	} -	//write the meta element -	if(!checkWrite(apr_file, &mMetaInfo, sizeof(HeaderMetaInfo))) +	if(mReadOnly)  	{ -		return ; +		llwarns << "Not writing cache header: Cache is currently in read-only mode." << llendl; +		return;  	} -	mNumEntries = 0 ; -	for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; iter != mHeaderEntryQueue.end(); ++iter) +	bool success = true ;  	{ -		(*iter)->mIndex = mNumEntries++ ; -		if(!checkWrite(apr_file, (void*)*iter, sizeof(HeaderEntryInfo))) +		LLAPRFile apr_file(mHeaderFileName, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); + +		//write the meta element +		success = check_write(&apr_file, &mMetaInfo, sizeof(HeaderMetaInfo)) ; + + +		mNumEntries = 0 ;	 +		for(header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; success && iter != mHeaderEntryQueue.end(); ++iter)  		{ -			return ; +			(*iter)->mIndex = mNumEntries++ ; +			success = check_write(&apr_file, (void*)*iter, sizeof(HeaderEntryInfo));  		} -	} - -	mNumEntries = mHeaderEntryQueue.size() ; -	if(mNumEntries < mCacheSize) -	{ -		HeaderEntryInfo* entry = new HeaderEntryInfo() ; -		for(U32 i = mNumEntries ; i < mCacheSize; i++) +	 +		mNumEntries = mHeaderEntryQueue.size() ; +		if(success && mNumEntries < MAX_NUM_OBJECT_ENTRIES)  		{ -			//fill the cache with the default entry. -			if(!checkWrite(apr_file, entry, sizeof(HeaderEntryInfo))) +			HeaderEntryInfo* entry = new HeaderEntryInfo() ; +			entry->mTime = INVALID_TIME ; +			for(S32 i = mNumEntries ; success && i < MAX_NUM_OBJECT_ENTRIES ; i++)  			{ -				mReadOnly = TRUE ; //disable the cache. -				return ; +				//fill the cache with the default entry. +				success = check_write(&apr_file, entry, sizeof(HeaderEntryInfo)) ;			 +  			} +			delete entry ;  		} -		delete entry ;  	} -	delete apr_file ; + +	if(!success) +	{ +		clearCacheInMemory() ; +		mReadOnly = TRUE ; //disable the cache. +	} +	return ;  }  BOOL LLVOCache::updateEntry(const HeaderEntryInfo* entry)  { -	LLAPRFile* apr_file = new LLAPRFile(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); -	apr_file->seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ; +	LLAPRFile apr_file(mHeaderFileName, APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); +	apr_file.seek(APR_SET, entry->mIndex * sizeof(HeaderEntryInfo) + sizeof(HeaderMetaInfo)) ; -	return checkWrite(apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ; +	return check_write(&apr_file, (void*)entry, sizeof(HeaderEntryInfo)) ;  }  void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map)   {  	if(!mEnabled)  	{ +		llwarns << "Not reading cache for handle " << handle << "): Cache is currently disabled." << llendl;  		return ;  	}  	llassert_always(mInitialized); @@ -522,65 +597,69 @@ void LLVOCache::readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::voca  	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;  	if(iter == mHandleEntryMap.end()) //no cache  	{ +		llwarns << "No handle map entry for " << handle << llendl;  		return ;  	} -	std::string filename; -	getObjectCacheFilename(handle, filename); -	LLAPRFile* apr_file = new LLAPRFile(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp); - -	LLUUID cache_id ; -	if(!checkRead(apr_file, cache_id.mData, UUID_BYTES)) +	bool success = true ;  	{ -		return ; -	} -	if(cache_id != id) -	{ -		llinfos << "Cache ID doesn't match for this region, discarding"<< llendl; - -		delete apr_file ; -		return ; -	} +		std::string filename; +		getObjectCacheFilename(handle, filename); +		LLAPRFile apr_file(filename, APR_READ|APR_BINARY, mLocalAPRFilePoolp); +	 +		LLUUID cache_id ; +		success = check_read(&apr_file, cache_id.mData, UUID_BYTES) ; +	 +		if(success) +		{		 +			if(cache_id != id) +			{ +				llinfos << "Cache ID doesn't match for this region, discarding"<< llendl; +				success = false ; +			} -	S32 num_entries; -	if(!checkRead(apr_file, &num_entries, sizeof(S32))) -	{ -		return ; +			if(success) +			{ +				S32 num_entries; +				success = check_read(&apr_file, &num_entries, sizeof(S32)) ; +	 +				for (S32 i = 0; success && i < num_entries; i++) +				{ +					LLVOCacheEntry* entry = new LLVOCacheEntry(&apr_file); +					if (!entry->getLocalID()) +					{ +						llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl; +						delete entry ; +						success = false ; +					} +					cache_entry_map[entry->getLocalID()] = entry; +				} +			} +		}		  	} -	for (S32 i = 0; i < num_entries; i++) +	if(!success)  	{ -		LLVOCacheEntry* entry = new LLVOCacheEntry(apr_file); -		if (!entry->getLocalID()) +		if(cache_entry_map.empty())  		{ -			llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl; -			delete entry ; -			break; +			removeEntry(iter->second) ;  		} -		cache_entry_map[entry->getLocalID()] = entry;  	} -	num_entries = cache_entry_map.size() ; -	delete apr_file ;  	return ;  } -void LLVOCache::purgeEntries() +void LLVOCache::purgeEntries(U32 size)  { -	U32 limit = mCacheSize - NUM_ENTRIES_TO_PURGE ; -	while(mHeaderEntryQueue.size() > limit) +	while(mHeaderEntryQueue.size() >= size)  	{  		header_entry_queue_t::iterator iter = mHeaderEntryQueue.begin() ; -		HeaderEntryInfo* entry = *iter ; -		 -		removeFromCache(entry->mHandle) ; -		mHandleEntryMap.erase(entry->mHandle) ;		 +		HeaderEntryInfo* entry = *iter ;			 +		mHandleEntryMap.erase(entry->mHandle);  		mHeaderEntryQueue.erase(iter) ; -		delete entry ; +		removeFromCache(entry) ; +		delete entry;  	} - -	writeCacheHeader() ; -	readCacheHeader() ;  	mNumEntries = mHandleEntryMap.size() ;  } @@ -588,80 +667,86 @@ void LLVOCache::writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry:  {  	if(!mEnabled)  	{ +		llwarns << "Not writing cache for handle " << handle << "): Cache is currently disabled." << llendl;  		return ;  	}  	llassert_always(mInitialized);  	if(mReadOnly)  	{ +		llwarns << "Not writing cache for handle " << handle << "): Cache is currently in read-only mode." << llendl;  		return ; -	} +	}	  	HeaderEntryInfo* entry;  	handle_entry_map_t::iterator iter = mHandleEntryMap.find(handle) ;  	if(iter == mHandleEntryMap.end()) //new entry -	{		 -		if(mNumEntries >= mCacheSize) +	{				 +		if(mNumEntries >= mCacheSize - 1)  		{ -			purgeEntries() ; +			purgeEntries(mCacheSize - 1) ;  		} -		 +  		entry = new HeaderEntryInfo();  		entry->mHandle = handle ;  		entry->mTime = time(NULL) ; -		entry->mIndex = mNumEntries++ ; +		entry->mIndex = mNumEntries++;  		mHeaderEntryQueue.insert(entry) ;  		mHandleEntryMap[handle] = entry ;  	}  	else  	{ -		entry = iter->second ; -		entry->mTime = time(NULL) ; +		// Update access time. +		entry = iter->second ;		  		//resort  		mHeaderEntryQueue.erase(entry) ; +		 +		entry->mTime = time(NULL) ;  		mHeaderEntryQueue.insert(entry) ;  	}  	//update cache header  	if(!updateEntry(entry))  	{ +		llwarns << "Failed to update cache header index " << entry->mIndex << ". handle = " << handle << llendl;  		return ; //update failed.  	}  	if(!dirty_cache)  	{ +		llwarns << "Skipping write to cache for handle " << handle << ": cache not dirty" << llendl;  		return ; //nothing changed, no need to update.  	}  	//write to cache file -	std::string filename; -	getObjectCacheFilename(handle, filename); -	LLAPRFile* apr_file = new LLAPRFile(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); -	 -	if(!checkWrite(apr_file, (void*)id.mData, UUID_BYTES)) +	bool success = true ;  	{ -		return ; -	} +		std::string filename; +		getObjectCacheFilename(handle, filename); +		LLAPRFile apr_file(filename, APR_CREATE|APR_WRITE|APR_BINARY, mLocalAPRFilePoolp); +	 +		success = check_write(&apr_file, (void*)id.mData, UUID_BYTES) ; -	S32 num_entries = cache_entry_map.size() ; -	if(!checkWrite(apr_file, &num_entries, sizeof(S32))) -	{ -		return ; +	 +		if(success) +		{ +			S32 num_entries = cache_entry_map.size() ; +			success = check_write(&apr_file, &num_entries, sizeof(S32)); +	 +			for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); success && iter != cache_entry_map.end(); ++iter) +			{ +				success = iter->second->writeToFile(&apr_file) ; +			} +		}  	} -	for (LLVOCacheEntry::vocache_entry_map_t::const_iterator iter = cache_entry_map.begin(); iter != cache_entry_map.end(); ++iter) +	if(!success)  	{ -		if(!iter->second->writeToFile(apr_file)) -		{ -			//failed -			delete apr_file ; -			removeCache() ; -			return ; -		} +		removeEntry(entry) ; +  	} -	delete apr_file ;  	return ;  } diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h index ed2bc8bafe..14e3b4c793 100644 --- a/indra/newview/llvocache.h +++ b/indra/newview/llvocache.h @@ -95,7 +95,12 @@ private:  	{  		bool operator()(const HeaderEntryInfo* lhs, const HeaderEntryInfo* rhs) const  		{ -			return lhs->mTime < rhs->mTime; // older entry in front of queue (set) +			if(lhs->mTime == rhs->mTime)  +			{ +				return lhs < rhs ; +			} +			 +			return lhs->mTime < rhs->mTime ; // older entry in front of queue (set)  		}  	};  	typedef std::set<HeaderEntryInfo*, header_entry_less> header_entry_queue_t; @@ -111,6 +116,7 @@ public:  	void readFromCache(U64 handle, const LLUUID& id, LLVOCacheEntry::vocache_entry_map_t& cache_entry_map) ;  	void writeToCache(U64 handle, const LLUUID& id, const LLVOCacheEntry::vocache_entry_map_t& cache_entry_map, BOOL dirty_cache) ; +	void removeEntry(U64 handle) ;  	void setReadOnly(BOOL read_only) {mReadOnly = read_only;}  @@ -118,15 +124,14 @@ private:  	void setDirNames(ELLPath location);	  	// determine the cache filename for the region from the region handle	  	void getObjectCacheFilename(U64 handle, std::string& filename); -	void removeFromCache(U64 handle); +	void removeFromCache(HeaderEntryInfo* entry);  	void readCacheHeader();  	void writeCacheHeader();  	void clearCacheInMemory();  	void removeCache() ; -	void purgeEntries(); +	void removeEntry(HeaderEntryInfo* entry) ; +	void purgeEntries(U32 size);  	BOOL updateEntry(const HeaderEntryInfo* entry); -	BOOL checkRead(LLAPRFile* apr_file, void* src, S32 n_bytes) ; -	BOOL checkWrite(LLAPRFile* apr_file, void* src, S32 n_bytes) ;  private:  	BOOL                 mEnabled; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 761e12020b..f67e3a9770 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -388,10 +388,12 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,  				// There's something bogus in the data that we're unpacking.  				dp->dumpBufferToLog();  				llwarns << "Flushing cache files" << llendl; -				std::string mask; -				mask = gDirUtilp->getDirDelimiter() + "*.slc"; -				gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), mask); -// 				llerrs << "Bogus TE data in " << getID() << ", crashing!" << llendl; + +				if(LLVOCache::hasInstance() && getRegion()) +				{ +					LLVOCache::getInstance()->removeEntry(getRegion()->getHandle()) ; +				} +				  				llwarns << "Bogus TE data in " << getID() << llendl;  			}  			else  @@ -667,11 +669,32 @@ void LLVOVolume::updateTextures()  	}  } +BOOL LLVOVolume::isVisible() const  +{ +	if(mDrawable.notNull() && mDrawable->isVisible()) +	{ +		return TRUE ; +	} + +	if(isAttachment()) +	{ +		LLViewerObject* objp = (LLViewerObject*)getParent() ; +		while(objp && !objp->isAvatar()) +		{ +			objp = (LLViewerObject*)objp->getParent() ; +		} + +		return objp && objp->mDrawable.notNull() && objp->mDrawable->isVisible() ; +	} + +	return FALSE ; +} +  void LLVOVolume::updateTextureVirtualSize()  {  	// Update the pixel area of all faces -	if(mDrawable.isNull() || !mDrawable->isVisible()) +	if(!isVisible())  	{  		return ;  	} @@ -2738,14 +2761,7 @@ void LLVOVolume::updateRadius()  BOOL LLVOVolume::isAttachment() const  { -	if (mState == 0) -	{ -		return FALSE; -	} -	else -	{ -		return TRUE; -	} +	return mState != 0 ;  }  BOOL LLVOVolume::isHUDAttachment() const diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index 1e9b9737b1..8b68e7c78a 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -102,6 +102,7 @@ public:  				void	animateTextures();  	/*virtual*/ BOOL	idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time); +	            BOOL    isVisible() const ;  	/*virtual*/ BOOL	isActive() const;  	/*virtual*/ BOOL	isAttachment() const;  	/*virtual*/ BOOL	isRootEdit() const; // overridden for sake of attachments treating themselves as a root object diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index a49dc1b59d..66a6ab5e94 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -287,6 +287,9 @@ BOOL LLPanelBodyPartsListItem::postBuild()  	addWidgetToRightSide("btn_lock");  	addWidgetToRightSide("btn_edit_panel"); +	setWidgetsVisible(false); +	reshapeWidgets(); +  	return TRUE;  } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 15477e0a80..59b526059b 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1967,12 +1967,12 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)  	if(drawablep && !drawablep->isDead())  	{ -	if (drawablep->isSpatialBridge()) -	{ +		if (drawablep->isSpatialBridge()) +		{  			const LLDrawable* root = ((LLSpatialBridge*) drawablep)->mDrawable;  			llassert(root); // trying to catch a bad assumption  			if (root && //  // this test may not be needed, see above -			    root->getVObj()->isAttachment()) +					root->getVObj()->isAttachment())  			{  				LLDrawable* rootparent = root->getParent();  				if (rootparent) // this IS sometimes NULL @@ -1980,24 +1980,24 @@ void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera)  					LLViewerObject *vobj = rootparent->getVObj();  					llassert(vobj); // trying to catch a bad assumption  					if (vobj) // this test may not be needed, see above -		{ +					{  						const LLVOAvatar* av = vobj->asAvatar(); -			if (av && av->isImpostor()) -			{ -				return; -			} -		} +						if (av && av->isImpostor()) +						{ +							return; +						} +					}  				}  			} -		sCull->pushBridge((LLSpatialBridge*) drawablep); -	} -	else -	{ -		sCull->pushDrawable(drawablep); -	} +			sCull->pushBridge((LLSpatialBridge*) drawablep); +		} +		else +		{ +			sCull->pushDrawable(drawablep); +		} -	drawablep->setVisible(camera); -} +		drawablep->setVisible(camera); +	}  }  void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion) diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 3f785a99fe..cef3d87f36 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -424,6 +424,7 @@ public:  		RENDER_DEBUG_AVATAR_VOLUME      = 0x0100000,  		RENDER_DEBUG_BUILD_QUEUE		= 0x0200000,  		RENDER_DEBUG_AGENT_TARGET       = 0x0400000, +		RENDER_DEBUG_UPDATE_TYPE		= 0x0800000,  	};  public: diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 62441fd984..75aec21f93 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -115,9 +115,6 @@       name="AlertCautionTextColor"       reference="LtYellow" />      <color -     name="AgentLinkColor" -     reference="EmphasisColor" /> -    <color       name="AlertTextColor"       value="0.58 0.66 0.84 1" />      <color @@ -349,9 +346,6 @@       name="GridlineShadowColor"       value="0 0 0 0.31" />      <color -     name="GroupLinkColor" -     reference="White" /> -    <color       name="GroupNotifyBoxColor"       value="0.3344 0.5456 0.5159 1" />      <color diff --git a/indra/newview/skins/default/xui/da/notifications.xml b/indra/newview/skins/default/xui/da/notifications.xml index 70299c61b4..27024f4eaa 100644 --- a/indra/newview/skins/default/xui/da/notifications.xml +++ b/indra/newview/skins/default/xui/da/notifications.xml @@ -1580,9 +1580,6 @@ Klik på Acceptér for at deltage eller Afvis for at afvise invitationen. Klik p  	<notification name="VoiceCallGenericError">  		En fejl er opstået under forsøget på at koble sig på stemme chatten [VOICE_CHANNEL_NAME].  Pråv venligst senere.  	</notification> -	<notification name="ServerVersionChanged"> -		Du er netop ankommet til en region der benytter en anden server version, hvilket kan influere på hastigheden. [[URL] For at se yderligere.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		Den SLurl du klikkede på understøttes ikke.  	</notification> @@ -1636,7 +1633,7 @@ Knappen vil blive vist når der er nok plads til den.  	<notification name="ShareItemsConfirmation">  		Er du sikker på at du vil dele følgende genstande: -[ITEMS] +<nolink>[ITEMS]</nolink>  Med følgende beboere: diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index bf525dd7c3..b0ad989a59 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -2717,9 +2717,6 @@ Klicken Sie auf  'Akzeptieren ', um dem Chat beizutreten, oder auf  &a  	<notification name="VoiceCallGenericError">  		Fehler beim Versuch, eine Voice-Chat-Verbindung zu [VOICE_CHANNEL_NAME] herzustellen.  Bitte versuchen Sie es erneut.  	</notification> -	<notification name="ServerVersionChanged"> -		Sie haben eine Region betreten, die eine andere Server-Version verwendet. Dies kann sich auf die Leistung auswirken. [[URL] Versionshinweise anzeigen.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		Die SLurl, auf die Sie geklickt haben, wird nicht unterstützt.  	</notification> @@ -2773,7 +2770,7 @@ Die Schaltfläche wird angezeigt, wenn genügend Platz vorhanden ist.  	<notification name="ShareItemsConfirmation">  		Möchten Sie diese Objekte wirklich für andere freigeben: -[ITEMS] +<nolink>[ITEMS]</nolink>  Für folgende Einwohner: diff --git a/indra/newview/skins/default/xui/en/floater_tools.xml b/indra/newview/skins/default/xui/en/floater_tools.xml index e70e1eb61b..1808fea445 100644 --- a/indra/newview/skins/default/xui/en/floater_tools.xml +++ b/indra/newview/skins/default/xui/en/floater_tools.xml @@ -872,12 +872,13 @@               length="1"               follows="left|top"               left_pad="0" -             height="30" +             height="20"               layout="topleft"               name="Creator Name"               top_delta="0"               width="190" -             word_wrap="true"> +             word_wrap="true" +             use_ellipses="ture">                  Mrs. Esbee Linden (esbee.linden)              </text>              <text @@ -888,7 +889,7 @@               height="19"               layout="topleft"               name="Owner:" -             top_pad="3" +             top_pad="13"               width="90">                  Owner:              </text> @@ -897,13 +898,14 @@               type="string"               length="1"               follows="left|top" -             height="30" +             height="20"               layout="topleft"               name="Owner Name"               left_pad="0"               top_delta="0"               width="190" -             word_wrap="true"> +             word_wrap="true" +             use_ellipses="true">                  Mrs. Erica "Moose" Linden (erica.linden)              </text>             <text @@ -914,7 +916,7 @@               left="10"               height="18"               name="Group:" -             top_pad="7" +             top_pad="17"               width="75">                  Group:              </text> diff --git a/indra/newview/skins/default/xui/en/floater_web_content.xml b/indra/newview/skins/default/xui/en/floater_web_content.xml index 456b2d4421..e04a72cbc0 100644 --- a/indra/newview/skins/default/xui/en/floater_web_content.xml +++ b/indra/newview/skins/default/xui/en/floater_web_content.xml @@ -1,190 +1,190 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 -<floater
 -  legacy_header_height="18"
 -  can_resize="true"
 -  height="775"
 -  layout="topleft"
 -  min_height="400"
 -  min_width="500"
 -  name="floater_web_content"
 -  help_topic="floater_web_content"
 -  save_rect="true"
 -  auto_tile="true"
 -  title=""
 -  initial_mime_type="text/html"
 -  width="780">
 -  <layout_stack
 -    bottom="775"
 -    follows="left|right|top|bottom"
 -    layout="topleft"
 -    left="5"
 -    name="stack1"
 -    orientation="vertical"
 -    top="20"
 -    width="770">
 -    <layout_panel
 -      auto_resize="false"
 -      default_tab_group="1"
 -      height="22"
 -      layout="topleft"
 -      left="0"
 -      min_height="20"
 -      name="nav_controls"
 -      top="400"
 -      user_resize="false"
 -      width="770">
 -      <button
 -        image_overlay="Arrow_Left_Off"
 -		    image_disabled="PushButton_Disabled"
 -		    image_disabled_selected="PushButton_Disabled"
 -		    image_selected="PushButton_Selected"
 -		    image_unselected="PushButton_Off"
 -		    hover_glow_amount="0.15"
 -        tool_tip="Navigate back"
 -        follows="left|top"
 -        height="22"
 -        layout="topleft"
 -        left="1"
 -        name="back"
 -        top="0"
 -        width="22">
 -        <button.commit_callback
 -          function="WebContent.Back" />
 -      </button>
 -      <button
 -        image_overlay="Arrow_Right_Off"
 -		    image_disabled="PushButton_Disabled"
 -		    image_disabled_selected="PushButton_Disabled"
 -		    image_selected="PushButton_Selected"
 -		    image_unselected="PushButton_Off"
 -        tool_tip="Navigate forward"
 -        follows="left|top"
 -        height="22"
 -        layout="topleft"
 -        left="27"
 -        name="forward"
 -        top_delta="0"
 -        width="22">
 -        <button.commit_callback
 -          function="WebContent.Forward" />
 -      </button>
 -      <button
 -        image_overlay="Stop_Off"
 -		    image_disabled="PushButton_Disabled"
 -		    image_disabled_selected="PushButton_Disabled"
 -		    image_selected="PushButton_Selected"
 -		    image_unselected="PushButton_Off"
 -        tool_tip="Stop navigation"
 -        enabled="true"
 -        follows="left|top"
 -        height="22"
 -        layout="topleft"
 -        left="51"
 -        name="stop"
 -        top_delta="0"
 -        width="22">
 -        <button.commit_callback
 -          function="WebContent.Stop" />
 -      </button>
 -      <button
 -        image_overlay="Refresh_Off"
 -		    image_disabled="PushButton_Disabled"
 -		    image_disabled_selected="PushButton_Disabled"
 -		    image_selected="PushButton_Selected"
 -		    image_unselected="PushButton_Off"
 -        tool_tip="Reload page"
 -        follows="left|top"
 -        height="22"
 -        layout="topleft"
 -        left="51"
 -        name="reload"
 -        top_delta="0"
 -        width="22">
 -        <button.commit_callback
 -          function="WebContent.Reload" />
 -      </button>
 -      <combo_box
 -        allow_text_entry="true"
 -        follows="left|top|right"
 -        tab_group="1"
 -        height="22"
 -        layout="topleft"
 -        left_pad="4"
 -        max_chars="1024"
 -        name="address"
 -        combo_editor.select_on_focus="true"
 -        tool_tip="Enter URL here"
 -        top_delta="0"
 -        width="672">
 -        <combo_box.commit_callback
 -          function="WebContent.EnterAddress" />
 -      </combo_box>
 -      <icon
 -        name="media_secure_lock_flag"
 -        height="16"
 -        follows="top|right"
 -        image_name="Lock2"
 -        layout="topleft"
 -        left_delta="620"
 -        top_delta="2"
 -        visible="false" 
 -        tool_tip="Secured Browsing"
 -        width="16" />
 -      <button
 -        image_overlay="ExternalBrowser_Off"
 -		    image_disabled="PushButton_Disabled"
 -		    image_disabled_selected="PushButton_Disabled"
 -		    image_selected="PushButton_Selected"
 -		    image_unselected="PushButton_Off"
 -        tool_tip="Open current URL in your desktop browser"
 -        follows="right|top"
 -        enabled="true" 
 -        height="22"
 -        layout="topleft"
 -        name="popexternal"
 -        right="770"
 -        top_delta="-2"
 -        width="22">
 -        <button.commit_callback
 -          function="WebContent.PopExternal" />
 -      </button>
 -    </layout_panel>
 -    <layout_panel
 -      height="40"
 -      layout="topleft"
 -      left_delta="0"
 -      name="external_controls"
 -      top_delta="0"
 -      user_resize="false"
 -      width="585">
 -      <web_browser
 -        bottom="-22"
 -        follows="all"
 -        layout="topleft"
 -        left="0"
 -        name="webbrowser"
 -        top="0"/>
 -      <text
 -        type="string"
 -        length="200"
 -        follows="bottom|left"
 -        height="20"
 -        layout="topleft"
 -        left_delta="0"
 -        name="statusbartext"
 -        parse_urls="false"
 -        text_color="0.4 0.4 0.4 1"
 -        top_pad="5"
 -        width="495"/>
 -      <progress_bar
 -        color_bar="0.3 1.0 0.3 1"
 -        follows="bottom|right"
 -        height="16"
 -        top_delta="-1"
 -        left_pad="24"
 -        layout="topleft"
 -        name="statusbarprogress"
 -        width="64"/>
 -    </layout_panel>
 -  </layout_stack>
 -</floater>
 +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater +  legacy_header_height="18" +  can_resize="true" +  height="775" +  layout="topleft" +  min_height="400" +  min_width="500" +  name="floater_web_content" +  help_topic="floater_web_content" +  save_rect="true" +  auto_tile="true" +  title="" +  initial_mime_type="text/html" +  width="780"> +  <layout_stack +    bottom="775" +    follows="left|right|top|bottom" +    layout="topleft" +    left="5" +    name="stack1" +    orientation="vertical" +    top="20" +    width="770"> +    <layout_panel +      auto_resize="false" +      default_tab_group="1" +      height="22" +      layout="topleft" +      left="0" +      min_height="20" +      name="nav_controls" +      top="400" +      user_resize="false" +      width="770"> +      <button +        image_overlay="Arrow_Left_Off" +		    image_disabled="PushButton_Disabled" +		    image_disabled_selected="PushButton_Disabled" +		    image_selected="PushButton_Selected" +		    image_unselected="PushButton_Off" +		    hover_glow_amount="0.15" +        tool_tip="Navigate back" +        follows="left|top" +        height="22" +        layout="topleft" +        left="1" +        name="back" +        top="0" +        width="22"> +        <button.commit_callback +          function="WebContent.Back" /> +      </button> +      <button +        image_overlay="Arrow_Right_Off" +		    image_disabled="PushButton_Disabled" +		    image_disabled_selected="PushButton_Disabled" +		    image_selected="PushButton_Selected" +		    image_unselected="PushButton_Off" +        tool_tip="Navigate forward" +        follows="left|top" +        height="22" +        layout="topleft" +        left="27" +        name="forward" +        top_delta="0" +        width="22"> +        <button.commit_callback +          function="WebContent.Forward" /> +      </button> +      <button +        image_overlay="Stop_Off" +		    image_disabled="PushButton_Disabled" +		    image_disabled_selected="PushButton_Disabled" +		    image_selected="PushButton_Selected" +		    image_unselected="PushButton_Off" +        tool_tip="Stop navigation" +        enabled="true" +        follows="left|top" +        height="22" +        layout="topleft" +        left="51" +        name="stop" +        top_delta="0" +        width="22"> +        <button.commit_callback +          function="WebContent.Stop" /> +      </button> +      <button +        image_overlay="Refresh_Off" +		    image_disabled="PushButton_Disabled" +		    image_disabled_selected="PushButton_Disabled" +		    image_selected="PushButton_Selected" +		    image_unselected="PushButton_Off" +        tool_tip="Reload page" +        follows="left|top" +        height="22" +        layout="topleft" +        left="51" +        name="reload" +        top_delta="0" +        width="22"> +        <button.commit_callback +          function="WebContent.Reload" /> +      </button> +      <combo_box +        allow_text_entry="true" +        follows="left|top|right" +        tab_group="1" +        height="22" +        layout="topleft" +        left_pad="4" +        max_chars="1024" +        name="address" +        combo_editor.select_on_focus="true" +        tool_tip="Enter URL here" +        top_delta="0" +        width="672"> +        <combo_box.commit_callback +          function="WebContent.EnterAddress" /> +      </combo_box> +      <icon +        name="media_secure_lock_flag" +        height="16" +        follows="top|right" +        image_name="Lock2" +        layout="topleft" +        left_delta="620" +        top_delta="2" +        visible="false"  +        tool_tip="Secured Browsing" +        width="16" /> +      <button +        image_overlay="ExternalBrowser_Off" +		    image_disabled="PushButton_Disabled" +		    image_disabled_selected="PushButton_Disabled" +		    image_selected="PushButton_Selected" +		    image_unselected="PushButton_Off" +        tool_tip="Open current URL in your desktop browser" +        follows="right|top" +        enabled="true"  +        height="22" +        layout="topleft" +        name="popexternal" +        right="770" +        top_delta="-2" +        width="22"> +        <button.commit_callback +          function="WebContent.PopExternal" /> +      </button> +    </layout_panel> +    <layout_panel +      height="40" +      layout="topleft" +      left_delta="0" +      name="external_controls" +      top_delta="0" +      user_resize="false" +      width="585"> +      <web_browser +        bottom="-22" +        follows="all" +        layout="topleft" +        left="0" +        name="webbrowser" +        top="0"/> +      <text +        type="string" +        length="200" +        follows="bottom|left" +        height="20" +        layout="topleft" +        left_delta="0" +        name="statusbartext" +        parse_urls="false" +        text_color="0.4 0.4 0.4 1" +        top_pad="5" +        width="495"/> +      <progress_bar +        color_bar="0.3 1.0 0.3 1" +        follows="bottom|right" +        height="16" +        top_delta="-1" +        left_pad="24" +        layout="topleft" +        name="statusbarprogress" +        width="64"/> +    </layout_panel> +  </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_object.xml b/indra/newview/skins/default/xui/en/menu_object.xml index c751aa4e0c..719509301b 100644 --- a/indra/newview/skins/default/xui/en/menu_object.xml +++ b/indra/newview/skins/default/xui/en/menu_object.xml @@ -100,7 +100,7 @@           name="Object Attach HUD" />     </context_menu>     <context_menu -         label="Remove" +         label="Manage"           name="Remove">     <menu_item_call           enabled="false" @@ -129,15 +129,6 @@           <menu_item_call.on_enable            function="Object.EnableReturn" />       </menu_item_call> -     <menu_item_call -   enabled="false" -   label="Delete" -   name="Delete"> -      <menu_item_call.on_click -       function="Object.Delete" /> -      <menu_item_call.on_enable -       function="Object.EnableDelete" /> -    </menu_item_call>      </context_menu>     <menu_item_separator layout="topleft" />     <menu_item_call @@ -176,4 +167,13 @@        <menu_item_call.on_enable         function="Object.EnableBuy" />     </menu_item_call> +   <menu_item_call +     enabled="false" +     label="Delete" +     name="Delete"> +      <menu_item_call.on_click +       function="Object.Delete" /> +      <menu_item_call.on_enable +       function="Object.EnableDelete" /> +  </menu_item_call>  </context_menu> diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml index 6f46165883..1aeb166e01 100644 --- a/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml +++ b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml @@ -25,6 +25,14 @@           function="Places.LandmarksGear.Enable"           parameter="category" />      </menu_item_call> +    <menu_item_call +     label="Restore Item" +     layout="topleft" +     name="restore_item"> +        <menu_item_call.on_click +         function="Places.LandmarksGear.Custom.Action" +         parameter="restore" /> +    </menu_item_call>      <menu_item_separator       layout="topleft" />      <menu_item_call diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml index 121e7cc07a..ff5fdd3795 100644 --- a/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml +++ b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml @@ -60,6 +60,14 @@           function="Places.LandmarksGear.Enable"           parameter="category" />      </menu_item_call> +    <menu_item_call +     label="Restore Item" +     layout="topleft" +     name="restore_item"> +        <menu_item_call.on_click +         function="Places.LandmarksGear.Custom.Action" +         parameter="restore" /> +    </menu_item_call>      <menu_item_separator       layout="topleft" />      <menu_item_call diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index d997a262a8..c2735c85e4 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -1960,6 +1960,16 @@                   function="ToggleControl"                   parameter="DebugShowRenderInfo" />              </menu_item_check> +			<menu_item_check +             label="Show Texture Info" +             name="Show Texture Info"> +                <menu_item_check.on_check +                 function="CheckControl" +                 parameter="DebugShowTextureInfo" /> +                <menu_item_check.on_click +                 function="ToggleControl" +                 parameter="DebugShowTextureInfo" /> +            </menu_item_check>              <menu_item_check               label="Show Matrices"               name="Show Matrices"> @@ -2153,6 +2163,16 @@             parameter="render batches" />          </menu_item_check>          <menu_item_check +         label="Update Type" +         name="Update Type"> +          <menu_item_check.on_check +           function="Advanced.CheckInfoDisplay" +           parameter="update type" /> +          <menu_item_check.on_click +           function="Advanced.ToggleInfoDisplay" +           parameter="update type" /> +        </menu_item_check> +        <menu_item_check           label="Texture Anim"           name="Texture Anim">            <menu_item_check.on_check @@ -2717,6 +2737,18 @@                   function="Floater.Toggle"                   parameter="region_debug_console" />              </menu_item_check> +            <menu_item_check +             label="Region Debug Console" +             name="Region Debug Console" +             shortcut="control|shift|`" +             use_mac_ctrl="true"> +                <menu_item_check.on_check +                 function="Floater.Visible" +                 parameter="region_debug_console" /> +                <menu_item_check.on_click +                 function="Floater.Toggle" +                 parameter="region_debug_console" /> +            </menu_item_check>              <menu_item_separator /> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 3df53ac442..f008042a81 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6304,15 +6304,6 @@ An error has occurred while trying to connect to voice chat for [VOICE_CHANNEL_N    </notification>    <notification -   duration="10" -   icon="notifytip.tga" -   name="ServerVersionChanged" -   priority="high" -   type="notifytip"> -You just entered a region using a different server version, which may affect performance. [[URL] View the release notes.] -  </notification> - -  <notification     icon="notifytip.tga"     name="UnsupportedCommandSLURL"     priority="high" @@ -6431,7 +6422,7 @@ Select residents to share with.     type="alertmodal">  Are you sure you want to share the following items: -[ITEMS] +<nolink>[ITEMS]</nolink>  With the following Residents: diff --git a/indra/newview/skins/default/xui/en/panel_group_land_money.xml b/indra/newview/skins/default/xui/en/panel_group_land_money.xml index 61d6cbb2d0..eff674c628 100644 --- a/indra/newview/skins/default/xui/en/panel_group_land_money.xml +++ b/indra/newview/skins/default/xui/en/panel_group_land_money.xml @@ -67,23 +67,23 @@          <scroll_list.columns           label="Parcel"           name="name" -         width="47" /> +         relative_width="0.2" />          <scroll_list.columns           label="Region"           name="location" -         width="47" /> +         relative_width="0.2" />          <scroll_list.columns           label="Type"           name="type" -         width="47" /> +         relative_width="0.2" />          <scroll_list.columns           label="Area"           name="area" -         width="47" /> +         relative_width="0.2" />          <scroll_list.columns           label="Hidden"           name="hidden" -         width="47" /> +         relative_width="0.2" />      </scroll_list>      <text       type="string" diff --git a/indra/newview/skins/default/xui/en/panel_nearby_media.xml b/indra/newview/skins/default/xui/en/panel_nearby_media.xml index 8c13ced8f3..9bd60b935f 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_media.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_media.xml @@ -68,24 +68,12 @@  		right="-8"  		width="66"  		height="22" +		is_toggle="true"  		label="More >>" -		label_selected="Less <<"> +		label_selected="<< Less">  	  <button.commit_callback  		  function="MediaListCtrl.MoreLess" />  	</button> -  <button -		name="less_btn" -		follows="right" -		tool_tip="Advanced Controls" -		top_delta="0" -		right="-8" -		width="66" -		height="22" -		label="More >>" -		label_selected="Less <<"> -      <button.commit_callback -        function="MediaListCtrl.MoreLess" /> -  </button>    </panel>    <panel  	  name="nearby_media_panel" diff --git a/indra/newview/skins/default/xui/en/widgets/floater.xml b/indra/newview/skins/default/xui/en/widgets/floater.xml index 85d0c633af..2e5ebafe46 100644 --- a/indra/newview/skins/default/xui/en/widgets/floater.xml +++ b/indra/newview/skins/default/xui/en/widgets/floater.xml @@ -21,4 +21,5 @@   tear_off_pressed_image="tearoff_pressed.tga"   dock_pressed_image="Icon_Dock_Press"   help_pressed_image="Icon_Help_Press" + focus_root="true"    /> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 051314bdfa..2bf36bb763 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -2706,9 +2706,6 @@ Pulsa Aceptar o Rehusar para coger o no la llamada. Pulsa Ignorar para ignorar a  	<notification name="VoiceCallGenericError">  		Se ha producido un error al intentar conectarte al [VOICE_CHANNEL_NAME]. Por favor, inténtalo más tarde.  	</notification> -	<notification name="ServerVersionChanged"> -		Acabas de entrar en una región que usa un servidor con una versión distinta, y esto puede influir en el funcionamiento. [[URL] Ver las notas de desarrollo]. -	</notification>  	<notification name="UnsupportedCommandSLURL">  		No se admite el formato de la SLurl que has pulsado.  	</notification> @@ -2762,7 +2759,7 @@ Se mostrará cuando haya suficiente espacio.  	<notification name="ShareItemsConfirmation">  		¿Estás seguro de que quieres compartir los elementos siguientes? -[ITEMS] +<nolink>[ITEMS]</nolink>  Con los siguientes residentes: diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index 140bbcc18b..603b8f0edc 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -2702,9 +2702,6 @@ Pour y participer, cliquez sur Accepter. Sinon, cliquez sur Refuser. Pour ignore  	<notification name="VoiceCallGenericError">  		Une erreur est survenue pendant la connexion au chat vocal pour [VOICE_CHANNEL_NAME]. Veuillez réessayer ultérieurement.  	</notification> -	<notification name="ServerVersionChanged"> -		La région dans laquelle vous avez pénétré utilise une version de serveur différente, ce qui peut avoir un impact sur votre performance. [[URL] Consultez les notes de version.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		La SLurl que vous avez saisie n'est pas prise en charge.  	</notification> @@ -2758,7 +2755,7 @@ Le bouton sera affiché quand il y aura suffisamment de place.  	<notification name="ShareItemsConfirmation">  		Voulez-vous vraiment partager les articles suivants : -[ITEMS] +<nolink>[ITEMS]</nolink>  avec les résidents suivants : diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml index 32483881b2..cce5888598 100644 --- a/indra/newview/skins/default/xui/it/notifications.xml +++ b/indra/newview/skins/default/xui/it/notifications.xml @@ -2623,9 +2623,6 @@ Clicca su Accetta per unirti alla chat oppure su Declina per declinare l'in  	<notification name="VoiceCallGenericError">  		Si è verificato un errore durante il tentativo di collegarti a una voice chat con [VOICE_CHANNEL_NAME].  Riprova più tardi.  	</notification> -	<notification name="ServerVersionChanged"> -		Sei appena entrato in una regione che usa una versione differente del server: ciò potrebbe incidere sule prestazioni. [[URL] Visualizza le note sulla versione.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		Lo SLurl su cui hai cliccato non è valido.  	</notification> @@ -2679,7 +2676,7 @@ Il pulsante verrà visualizzato quando lo spazio sarà sufficiente.  	<notification name="ShareItemsConfirmation">  		Sei sicuro di volere condividere gli oggetti -[ITEMS] +<nolink>[ITEMS]</nolink>  Con i seguenti residenti? diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index c0af0e03ff..baec8c073c 100644 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -2675,9 +2675,6 @@ M キーを押して変更します。  	<notification name="VoiceCallGenericError">  		[VOICE_CHANNEL_NAME] のボイスチャットに接続中に、エラーが発生しました。後でもう一度お試しください。  	</notification> -	<notification name="ServerVersionChanged"> -		サーバーのバージョンが異なるリージョンに来ました。パフォーマンスに影響することがあります。 [[URL] リリースノートを確認] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		クリックした SLurl はサポートされていません。  	</notification> @@ -2731,7 +2728,7 @@ M キーを押して変更します。  	<notification name="ShareItemsConfirmation">  		次のアイテムを共有しますか: -[ITEMS] +<nolink>[ITEMS]</nolink>  次の住人と共有しますか: diff --git a/indra/newview/skins/default/xui/nl/notifications.xml b/indra/newview/skins/default/xui/nl/notifications.xml index be0c17d2ff..f27b83d3f9 100644 --- a/indra/newview/skins/default/xui/nl/notifications.xml +++ b/indra/newview/skins/default/xui/nl/notifications.xml @@ -3012,9 +3012,6 @@ Klik Accepteren om deel te nemen aan de chat of Afwijzen om de uitnodiging af te  	<notification name="VoiceCallGenericError">  		Er is een fout opgetreden tijdens het verbinden met voice chat voor [VOICE_CHANNEL_NAME]. Probeert u het later alstublieft opnieuw.  	</notification> -	<notification name="ServerVersionChanged"> -		De regio die u bent binnengetreden wordt onder een andere simulatorversie uitgevoerd. Klik dit bericht voor meer details. -	</notification>  	<notification name="UnableToOpenCommandURL">  		De URL die u heeft geklikt kan niet binnen deze webbrowser worden geopend.  	</notification> diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml index 8dc4b041cd..25fa5da3ab 100644 --- a/indra/newview/skins/default/xui/pl/notifications.xml +++ b/indra/newview/skins/default/xui/pl/notifications.xml @@ -2672,9 +2672,6 @@ Wybierz Zaakceptuj żeby zacząć czat albo Odmów żeby nie przyjąć zaproszen  	<notification name="VoiceCallGenericError">  		Błąd podczas łączenia z rozmową [VOICE_CHANNEL_NAME]. Spróbuj póżniej.  	</notification> -	<notification name="ServerVersionChanged"> -		Ten region używa innej wersji symulatora. Kliknij na tą wiadomość żeby uzyskać więcej informacji: [[URL] View the release notes.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		Nie można otworzyć wybranego SLurl.  	</notification> @@ -2728,7 +2725,7 @@ Przycisk zostanie wyświetlony w przypadku dostatecznej ilości przestrzeni.  	<notification name="ShareItemsConfirmation">  		Jesteś pewien/pewna, że chcesz udostępnić następujące obiekty: -[ITEMS] +<nolink>[ITEMS]</nolink>  następującym Rezydentom: diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index 5f09397ac5..4b2e4bc5e0 100644 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -481,7 +481,7 @@ Para aumentar a qualidade do vídeo, vá para Preferências > Vídeo.  	</notification>  	<notification name="CannotCopyWarning">  		Você não tem autorização para copiar os itens abaixo: -[ITENS] +[ITEMS]  ao dá-los, você ficará sem eles no seu inventário. Deseja realmente dar estes itens?  		<usetemplate name="okcancelbuttons" notext="Não" yestext="Sim"/>  	</notification> @@ -2686,9 +2686,6 @@ Clique em Aceitar para atender ou em Recusar para recusar este convite.  Clique  	<notification name="VoiceCallGenericError">  		Ocorreu um erro enquanto você tentava se conectar à conversa de voz de [VOICE_CHANNEL_NAME].  Favor tentar novamente mais tarde.  	</notification> -	<notification name="ServerVersionChanged"> -		Você chegou a uma região com uma versão diferente de servidor, que pode afetar o desempenho.  [[URL] Consultar notas da versão.] -	</notification>  	<notification name="UnsupportedCommandSLURL">  		O SLurl no qual você clicou não é suportado.  	</notification> @@ -2742,7 +2739,7 @@ O botão será exibido quando houver espaço suficente.  	<notification name="ShareItemsConfirmation">  		Tem certeza de que quer compartilhar os items abaixo? -[ITENS] +<nolink>[ITEMS]</nolink>  Com os seguintes residentes: diff --git a/indra/newview/tests/llcapabilitylistener_test.cpp b/indra/newview/tests/llcapabilitylistener_test.cpp index 9da851ffc4..d691bb6c44 100644 --- a/indra/newview/tests/llcapabilitylistener_test.cpp +++ b/indra/newview/tests/llcapabilitylistener_test.cpp @@ -72,7 +72,7 @@ struct TestCapabilityProvider: public LLCapabilityProvider      {          mCaps[cap] = url;      } -    LLHost getHost() const { return mHost; } +    const LLHost& getHost() const { return mHost; }      std::string getDescription() const { return "TestCapabilityProvider"; }      LLHost mHost; diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index a6c1f69c82..7862cce3a1 100644 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -1,134 +1,136 @@ -/** 
 - * @file llremoteparcelrequest_test.cpp
 - * @author Brad Kittenbrink <brad@lindenlab.com>
 - *
 - * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 - * Second Life Viewer Source Code
 - * Copyright (C) 2010, Linden Research, Inc.
 - * 
 - * This library is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU Lesser General Public
 - * License as published by the Free Software Foundation;
 - * version 2.1 of the License only.
 - * 
 - * This library is distributed in the hope that it will be useful,
 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 - * Lesser General Public License for more details.
 - * 
 - * You should have received a copy of the GNU Lesser General Public
 - * License along with this library; if not, write to the Free Software
 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 - * 
 - * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 - * $/LicenseInfo$
 - */
 -
 -#include "linden_common.h"
 -
 -#include "../test/lltut.h"
 -
 -#include "../llremoteparcelrequest.h"
 -
 -#include "../llagent.h"
 -#include "message.h"
 -
 -namespace {
 -	LLControlGroup s_saved_settings("dummy_settings");
 -	const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111");
 -}
 -
 -LLCurl::Responder::Responder() { }
 -LLCurl::Responder::~Responder() { }
 -void LLCurl::Responder::error(U32,std::string const &) { }
 -void LLCurl::Responder::result(LLSD const &) { }
 -void LLCurl::Responder::errorWithContent(U32 status,std::string const &,LLSD const &) { }
 -void LLCurl::Responder::completedRaw(U32 status, std::string const &, LLChannelDescriptors const &,boost::shared_ptr<LLBufferArray> const &) { }
 -void LLCurl::Responder::completed(U32 status, std::string const &, LLSD const &) { }
 -void LLCurl::Responder::completedHeader(U32 status, std::string const &, LLSD const &) { }
 -void LLMessageSystem::getF32(char const *,char const *,F32 &,S32) { }
 -void LLMessageSystem::getU8(char const *,char const *,U8 &,S32) { }
 -void LLMessageSystem::getS32(char const *,char const *,S32 &,S32) { }
 -void LLMessageSystem::getString(char const *,char const *, std::string &,S32) { }
 -void LLMessageSystem::getUUID(char const *,char const *, LLUUID & out_id,S32)
 -{
 -	out_id = TEST_PARCEL_ID;
 -}
 -void LLMessageSystem::nextBlock(char const *) { }
 -void LLMessageSystem::addUUID(char const *,LLUUID const &) { }
 -void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { }
 -void LLMessageSystem::nextBlockFast(char const *) { }
 -void LLMessageSystem::newMessage(char const *) { }
 -LLMessageSystem * gMessageSystem;
 -char * _PREHASH_AgentID;
 -char * _PREHASH_AgentData;
 -LLAgent gAgent;
 -LLAgent::LLAgent() : mAgentAccess(s_saved_settings) { }
 -LLAgent::~LLAgent() { }
 -void LLAgent::sendReliableMessage(void) { }
 -LLUUID gAgentSessionID;
 -LLUUID gAgentID;
 -LLUIColor::LLUIColor(void) { }
 -LLAgentAccess::LLAgentAccess(LLControlGroup & settings) : mSavedSettings(settings) { }
 -LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker<LLControlGroup, std::string>(name) { }
 -LLControlGroup::~LLControlGroup(void) { }
 -
 -namespace tut
 -{
 -	struct TestObserver : public LLRemoteParcelInfoObserver {
 -		TestObserver() : mProcessed(false) { }
 -
 -		virtual void processParcelInfo(const LLParcelData& parcel_data)
 -		{
 -			mProcessed = true;
 -		}
 -
 -		virtual void setParcelID(const LLUUID& parcel_id) { }
 -
 -		virtual void setErrorStatus(U32 status, const std::string& reason) { }
 -
 -		bool mProcessed;
 -	};
 -
 -    struct RemoteParcelRequestData
 -    {
 -		RemoteParcelRequestData()
 -		{
 -		}
 -    };
 -    
 -	typedef test_group<RemoteParcelRequestData> remoteparcelrequest_t;
 -	typedef remoteparcelrequest_t::object remoteparcelrequest_object_t;
 -	tut::remoteparcelrequest_t tut_remoteparcelrequest("LLRemoteParcelRequest");
 -
 -	template<> template<>
 -	void remoteparcelrequest_object_t::test<1>()
 -	{
 -		set_test_name("observer pointer");
 -
 -		boost::scoped_ptr<TestObserver> observer(new TestObserver());
 -
 -		LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance();
 -		processor.addObserver(LLUUID(TEST_PARCEL_ID), observer.get());
 -
 -		processor.processParcelInfoReply(gMessageSystem, NULL);
 -
 -		ensure(observer->mProcessed);
 -	}
 -
 -	template<> template<>
 -	void remoteparcelrequest_object_t::test<2>()
 -	{
 -		set_test_name("CHOP-220: dangling observer pointer");
 -
 -		LLRemoteParcelInfoObserver * observer = new TestObserver();
 -
 -		LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance();
 -		processor.addObserver(LLUUID(TEST_PARCEL_ID), observer);
 -
 -		delete observer;
 -		observer = NULL;
 -
 -		processor.processParcelInfoReply(gMessageSystem, NULL);
 -	}
 -}
 +/**  + * @file llremoteparcelrequest_test.cpp + * @author Brad Kittenbrink <brad@lindenlab.com> + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "../test/lltut.h" + +#include "../llremoteparcelrequest.h" + +#include "../llagent.h" +#include "message.h" +#include "llurlentry.h" + +namespace { +	LLControlGroup s_saved_settings("dummy_settings"); +	const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111"); +} + +LLCurl::Responder::Responder() { } +LLCurl::Responder::~Responder() { } +void LLCurl::Responder::error(U32,std::string const &) { } +void LLCurl::Responder::result(LLSD const &) { } +void LLCurl::Responder::errorWithContent(U32 status,std::string const &,LLSD const &) { } +void LLCurl::Responder::completedRaw(U32 status, std::string const &, LLChannelDescriptors const &,boost::shared_ptr<LLBufferArray> const &) { } +void LLCurl::Responder::completed(U32 status, std::string const &, LLSD const &) { } +void LLCurl::Responder::completedHeader(U32 status, std::string const &, LLSD const &) { } +void LLMessageSystem::getF32(char const *,char const *,F32 &,S32) { } +void LLMessageSystem::getU8(char const *,char const *,U8 &,S32) { } +void LLMessageSystem::getS32(char const *,char const *,S32 &,S32) { } +void LLMessageSystem::getString(char const *,char const *, std::string &,S32) { } +void LLMessageSystem::getUUID(char const *,char const *, LLUUID & out_id,S32) +{ +	out_id = TEST_PARCEL_ID; +} +void LLMessageSystem::nextBlock(char const *) { } +void LLMessageSystem::addUUID(char const *,LLUUID const &) { } +void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { } +void LLMessageSystem::nextBlockFast(char const *) { } +void LLMessageSystem::newMessage(char const *) { } +LLMessageSystem * gMessageSystem; +char * _PREHASH_AgentID; +char * _PREHASH_AgentData; +LLAgent gAgent; +LLAgent::LLAgent() : mAgentAccess(s_saved_settings) { } +LLAgent::~LLAgent() { } +void LLAgent::sendReliableMessage(void) { } +LLUUID gAgentSessionID; +LLUUID gAgentID; +LLUIColor::LLUIColor(void) { } +LLAgentAccess::LLAgentAccess(LLControlGroup & settings) : mSavedSettings(settings) { } +LLControlGroup::LLControlGroup(std::string const & name) : LLInstanceTracker<LLControlGroup, std::string>(name) { } +LLControlGroup::~LLControlGroup(void) { } +void LLUrlEntryParcel::processParcelInfo(const LLUrlEntryParcel::LLParcelData& parcel_data) { } + +namespace tut +{ +	struct TestObserver : public LLRemoteParcelInfoObserver { +		TestObserver() : mProcessed(false) { } + +		virtual void processParcelInfo(const LLParcelData& parcel_data) +		{ +			mProcessed = true; +		} + +		virtual void setParcelID(const LLUUID& parcel_id) { } + +		virtual void setErrorStatus(U32 status, const std::string& reason) { } + +		bool mProcessed; +	}; + +    struct RemoteParcelRequestData +    { +		RemoteParcelRequestData() +		{ +		} +    }; +     +	typedef test_group<RemoteParcelRequestData> remoteparcelrequest_t; +	typedef remoteparcelrequest_t::object remoteparcelrequest_object_t; +	tut::remoteparcelrequest_t tut_remoteparcelrequest("LLRemoteParcelRequest"); + +	template<> template<> +	void remoteparcelrequest_object_t::test<1>() +	{ +		set_test_name("observer pointer"); + +		boost::scoped_ptr<TestObserver> observer(new TestObserver()); + +		LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); +		processor.addObserver(LLUUID(TEST_PARCEL_ID), observer.get()); + +		processor.processParcelInfoReply(gMessageSystem, NULL); + +		ensure(observer->mProcessed); +	} + +	template<> template<> +	void remoteparcelrequest_object_t::test<2>() +	{ +		set_test_name("CHOP-220: dangling observer pointer"); + +		LLRemoteParcelInfoObserver * observer = new TestObserver(); + +		LLRemoteParcelInfoProcessor & processor = LLRemoteParcelInfoProcessor::instance(); +		processor.addObserver(LLUUID(TEST_PARCEL_ID), observer); + +		delete observer; +		observer = NULL; + +		processor.processParcelInfoReply(gMessageSystem, NULL); +	} +}  | 
