diff options
| author | Oz Linden <oz@lindenlab.com> | 2014-03-03 15:36:34 -0500 | 
|---|---|---|
| committer | Oz Linden <oz@lindenlab.com> | 2014-03-03 15:36:34 -0500 | 
| commit | a5e56067c28298b4df0ef498303f78f67c35782c (patch) | |
| tree | ec5cf26d0eb8825a6d30fcf325e4862ddc353890 /indra/newview | |
| parent | 197db4976ad8df958b7b2c59a66498398cfd0665 (diff) | |
| parent | de8fea13627cc5978b8a6135802a52864a11c39a (diff) | |
merge changes for 3.7.2-release
Diffstat (limited to 'indra/newview')
65 files changed, 3376 insertions, 1301 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c5e1cde4e6..17e340d136 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -267,6 +267,7 @@ set(viewer_SOURCE_FILES      llfloaterregiondebugconsole.cpp      llfloaterregioninfo.cpp      llfloaterreporter.cpp +    llfloaterregionrestarting.cpp      llfloaterscriptdebug.cpp      llfloaterscriptlimits.cpp      llfloatersearch.cpp @@ -855,6 +856,7 @@ set(viewer_HEADER_FILES      llfloaterregiondebugconsole.h      llfloaterregioninfo.h      llfloaterreporter.h +    llfloaterregionrestarting.h      llfloaterscriptdebug.h      llfloaterscriptlimits.h      llfloatersearch.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index f5c2a4050b..c1e43e6d45 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.6.14 +3.7.3 diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index ce878f156b..60c942094a 100755 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -3,8 +3,6 @@    <command name="aboutland"             available_in_toybox="true"             icon="Command_AboutLand_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_AboutLand_Label"             tooltip_ref="Command_AboutLand_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -15,8 +13,6 @@    <command name="appearance"               available_in_toybox="true"             icon="Command_Appearance_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Appearance_Label"             tooltip_ref="Command_Appearance_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -27,8 +23,6 @@    <command name="avatar"             available_in_toybox="true"             icon="Command_Avatar_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Avatar_Label"             tooltip_ref="Command_Avatar_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -39,8 +33,6 @@    <command name="build"             available_in_toybox="true"             icon="Command_Build_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Build_Label"             tooltip_ref="Command_Build_Tooltip"             execute_function="Build.Toggle" @@ -54,8 +46,6 @@             available_in_toybox="true"  		   is_flashing_allowed="true"             icon="Command_Chat_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Chat_Label"             tooltip_ref="Command_Conversations_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -66,8 +56,6 @@    <command name="compass"             available_in_toybox="false"             icon="Command_Compass_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Compass_Label"             tooltip_ref="Command_Compass_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -78,8 +66,6 @@    <command name="destinations"             available_in_toybox="true"             icon="Command_Destinations_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Destinations_Label"             tooltip_ref="Command_Destinations_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -90,8 +76,6 @@    <command name="gestures"             available_in_toybox="true"             icon="Command_Gestures_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Gestures_Label"             tooltip_ref="Command_Gestures_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -102,8 +86,6 @@    <command name="howto"             available_in_toybox="true"             icon="Command_HowTo_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_HowTo_Label"             tooltip_ref="Command_HowTo_Tooltip"             execute_function="Help.ToggleHowTo" @@ -112,8 +94,6 @@    <command name="inventory"             available_in_toybox="true"             icon="Command_Inventory_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Inventory_Label"             tooltip_ref="Command_Inventory_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -124,8 +104,6 @@    <command name="map"             available_in_toybox="true"             icon="Command_Map_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Map_Label"             tooltip_ref="Command_Map_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -136,8 +114,6 @@    <command name="marketplace"             available_in_toybox="false"             icon="Command_Marketplace_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Marketplace_Label"             tooltip_ref="Command_Marketplace_Tooltip"             execute_function="Avatar.OpenMarketplace" @@ -145,8 +121,6 @@    <command name="minimap"             available_in_toybox="true"             icon="Command_MiniMap_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_MiniMap_Label"             tooltip_ref="Command_MiniMap_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -157,8 +131,6 @@    <command name="move"             available_in_toybox="true"             icon="Command_Move_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Move_Label"             tooltip_ref="Command_Move_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -169,8 +141,6 @@    <command name="outbox"             available_in_toybox="false"             icon="Command_Outbox_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Outbox_Label"             tooltip_ref="Command_Outbox_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -181,8 +151,6 @@    <command name="people"             available_in_toybox="true"             icon="Command_People_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_People_Label"             tooltip_ref="Command_People_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -193,8 +161,6 @@    <command name="picks"             available_in_toybox="true"             icon="Command_Picks_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Picks_Label"             tooltip_ref="Command_Picks_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -205,8 +171,6 @@    <command name="places"             available_in_toybox="true"             icon="Command_Places_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Places_Label"             tooltip_ref="Command_Places_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -217,8 +181,6 @@    <command name="preferences"             available_in_toybox="true"             icon="Command_Preferences_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Preferences_Label"             tooltip_ref="Command_Preferences_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -229,8 +191,6 @@    <command name="profile"             available_in_toybox="true"             icon="Command_Profile_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Profile_Label"             tooltip_ref="Command_Profile_Tooltip"             execute_function="Avatar.ToggleMyProfile" @@ -239,8 +199,6 @@    <command name="search"             available_in_toybox="true"             icon="Command_Search_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Search_Label"             tooltip_ref="Command_Search_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -251,8 +209,6 @@    <command name="snapshot"             available_in_toybox="true"             icon="Command_Snapshot_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Snapshot_Label"             tooltip_ref="Command_Snapshot_Tooltip"             execute_function="Floater.ToggleOrBringToFront" @@ -273,8 +229,6 @@    <command name="speak"             available_in_toybox="true"             icon="Command_Speak_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_Speak_Label"             tooltip_ref="Command_Speak_Tooltip"             execute_function="Agent.PressMicrophone" @@ -289,8 +243,6 @@    <command name="view"             available_in_toybox="true"             icon="Command_View_Icon" -           hover_icon_unselected="Command_Highlighting_Icon" -           hover_icon_selected="Command_Highlighting_Selected_Icon"             label_ref="Command_View_Label"             tooltip_ref="Command_View_Tooltip"             execute_function="Floater.ToggleOrBringToFront" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7e9971bc7d..a4c173897c 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10024,11 +10024,21 @@      <key>Value</key>      <real>16</real>    </map> - +  <key>Mesh2MaxConcurrentRequests</key> +  <map> +    <key>Comment</key> +    <string>Number of connections to use for loading meshes.</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>U32</string> +    <key>Value</key> +    <integer>8</integer> +  </map>    <key>MeshMaxConcurrentRequests</key>    <map>      <key>Comment</key> -    <string>Number of threads to use for loading meshes.</string> +    <string>Number of connections to use for loading meshes (legacy system).</string>      <key>Persist</key>      <integer>1</integer>      <key>Type</key> @@ -10036,6 +10046,28 @@      <key>Value</key>      <integer>32</integer>    </map> +  <key>MeshUseHttpRetryAfter</key> +  <map> +    <key>Comment</key> +    <string>If TRUE, use Retry-After response headers when rescheduling a mesh request that fails with an HTTP 503 status.  Static.</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <boolean>1</boolean> +  </map> +  <key>MeshUseGetMesh1</key> +  <map> +    <key>Comment</key> +    <string>If TRUE, use the legacy GetMesh capability for mesh download requests.  Semi-dynamic (read at region crossings).</string> +    <key>Persist</key> +    <integer>1</integer> +    <key>Type</key> +    <string>Boolean</string> +    <key>Value</key> +    <boolean>0</boolean> +  </map>     <key>RunMultipleThreads</key>      <map>        <key>Comment</key> @@ -12577,6 +12609,17 @@        <key>Value</key>        <string>00000000-0000-0000-0000-000000000000</string>      </map> +    <key>UISndRestart</key> +    <map> +      <key>Comment</key> +      <string>Sound file for region restarting (uuid for sound asset)</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>b92a0f64-7709-8811-40c5-16afd624a45f</string> +    </map>      <key>UISndSnapshot</key>      <map>        <key>Comment</key> @@ -15157,17 +15200,6 @@      <key>Value</key>      <integer>0</integer>    </map> -  <key>DisablePrecacheDelayAfterTeleporting</key> -  <map> -    <key>Comment</key> -    <string>Disables the artificial delay in the viewer that precaches some incoming assets</string> -    <key>Persist</key> -    <integer>0</integer> -    <key>Type</key> -    <string>Boolean</string> -    <key>Value</key> -    <integer>0</integer> -  </map>    <key>FMODExProfilerEnable</key>    <map>      <key>Comment</key> diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl index 39632d0cef..57129c3bd1 100755 --- a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -22,30 +22,46 @@   * $/LicenseInfo$   */ - -  ATTRIBUTE vec4 weight4;   -uniform mat4 matrixPalette[32]; +uniform mat3 matrixPalette[52]; +uniform vec3 translationPalette[52];  mat4 getObjectSkinnedTransform()  { -	int i;  +	int i;  	vec4 w = fract(weight4);  	vec4 index = floor(weight4); -		 index = min(index, vec4(31.0)); +		 index = min(index, vec4(51.0));  		 index = max(index, vec4( 0.0));  	float scale = 1.0/(w.x+w.y+w.z+w.w);  	w *= scale; -	mat4 mat = matrixPalette[int(index.x)]*w.x; -	mat += matrixPalette[int(index.y)]*w.y; -	mat += matrixPalette[int(index.z)]*w.z; -	mat += matrixPalette[int(index.w)]*w.w; +	int i1 = int(index.x); +	int i2 = int(index.y); +	int i3 = int(index.z); +	int i4 = int(index.w); -	return mat; +	mat3 mat  = matrixPalette[i1]*w.x; +		 mat += matrixPalette[i2]*w.y; +		 mat += matrixPalette[i3]*w.z; +		 mat += matrixPalette[i4]*w.w; + +	vec3 trans = translationPalette[i1]*w.x; +	trans += translationPalette[i2]*w.y; +	trans += translationPalette[i3]*w.z; +	trans += translationPalette[i4]*w.w; + +	mat4 ret; + +	ret[0] = vec4(mat[0], 0); +	ret[1] = vec4(mat[1], 0); +	ret[2] = vec4(mat[2], 0); +	ret[3] = vec4(trans, 1.0); +				 +	return ret;  } diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl index e5f7366b70..2b5f001873 100755 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl @@ -534,6 +534,7 @@ void main()  #ifdef FOR_IMPOSTOR  	vec4 color;  	color.rgb = diff.rgb; +	color.a = 1.0;  #ifdef USE_VERTEX_COLOR  	float final_alpha = diff.a * vertex_color.a; diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml index e5b385f4aa..5268498d56 100755 --- a/indra/newview/character/avatar_lad.xml +++ b/indra/newview/character/avatar_lad.xml @@ -3825,7 +3825,11 @@          <volume_morph            name="BELLY"            scale="0.075 0.04 0.03" -          pos="0.07 0 -0.07"/> +          pos="0.07 0 -0.02"/> +        <volume_morph +          name="PELVIS" +          scale="0.075 0.04 0.03" +          pos="0.07 0 -0.02"/>        </param_morph>      </param> @@ -3844,7 +3848,16 @@       camera_elevation=".1"       camera_distance="1"       camera_angle="15"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0273 0.0273 0.0273" +          pos="0.038 0.024 -0.016"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0273 0.0273 0.0273" +          pos="0.038 -0.024 -0.016"/> +	  </param_morph>      </param>      <param @@ -3861,7 +3874,16 @@       value_max="1"       camera_elevation="0"       camera_distance=".28"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="-0.05 0.0 0.0" +          pos="-0.01 -0.01 -0.02"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="-0.05 0.0 0.0" +          pos="-0.01 -0.01 -0.02"/> +	  </param_morph>      </param>      <param @@ -3878,7 +3900,16 @@       value_max="1"       camera_elevation="0"       camera_distance=".28"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="-0.051 0.0 0.0" +          pos="-0.02 -0.01 -0.03"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="-0.051 0.0 0.0" +          pos="-0.02 -0.01 -0.03"/> +	  </param_morph>      </param>      <param @@ -3944,6 +3975,10 @@            scale="0.0 -0.01 0.0"            pos="0.0 0.0 0"/>          <volume_morph +          name="UPPER_BACK" +          scale="-0.01 -0.01 0.0" +          pos="0.0 0.0 0"/> +        <volume_morph            name="CHEST"            scale="-0.01 -0.01 0.0"            pos="0.01 0.0 0"/> @@ -3994,6 +4029,10 @@            scale="-0.01 -0.01 0.0"            pos="0.01 0.0 0"/>          <volume_morph +          name="UPPER_BACK" +          scale="-0.01 -0.01 0.0" +          pos="0.0 0.0 0"/> +        <volume_morph            name="CHEST"            scale="-0.02 -0.02 0.0"            pos="0.01 0.0 0"/> @@ -4042,6 +4081,32 @@            scale="0.02 0.03 0.03"            pos="0 0 -0.03"/>          <volume_morph +          name="PELVIS" +          scale="0.02 0.03 0.03" +          pos="0 0 -0.03"/> +        <volume_morph +          name="UPPER_BACK" +          scale="0.01 0.03 0.0" +          pos="-0.03 0 0"/> +        <volume_morph +          name="LOWER_BACK" +          scale="0.04 0.06 0.0" +          pos="-0.06 0 0"/> +        <volume_morph +          name="LEFT_HANDLE" +          pos="0.0 0.08 0.0"/> +        <volume_morph +          name="RIGHT_HANDLE" +          pos="0.0 -0.08 0.0"/> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0367 0.0367 0.016" +          pos="0.00 -0.005 -0.013"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0367 0.0367 0.016" +          pos="0.00 0.005 -0.013"/> +        <volume_morph            name="BELLY"            scale="0.09 0.08 0.07"            pos="0 0 -0.05"/> @@ -4093,7 +4158,16 @@       value_max="2"       camera_elevation=".3"       camera_distance=".8"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.004 0.0 -0.01"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.004 0.0 -0.01"/> +      </param_morph>      </param>      <param @@ -4143,6 +4217,15 @@          <volume_morph            name="BELLY"            scale="0.0 0.02 0.0"/> +        <volume_morph +          name="LOWER_BACK" +          scale="0.0 0.02 0.0"/> +        <volume_morph +          name="LEFT_HANDLE" +          pos="0.0 0.025 0.0"/> +        <volume_morph +          name="RIGHT_HANDLE" +          pos="0.0 -0.025 0.0"/>        </param_morph>      </param> @@ -4162,7 +4245,16 @@       value_max="1.3"       camera_elevation=".3"       camera_distance=".8"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 -0.026 0.0"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.026 0.0"/> +      </param_morph>      </param>      <param @@ -4177,11 +4269,20 @@       label_min="Big Pectorals"       label_max="Sunken Chest"       value_default="0" -     value_min="-.5" +     value_min="-1.0"       value_max="1.1"       camera_elevation=".3"       camera_distance="1.2"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="-0.03 -0.024 -0.01"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="-0.03 0.024 -0.01"/> +      </param_morph>      </param>      <!-- ############# #  @@ -4206,6 +4307,14 @@            scale="0.03 0.03 0.0"            pos="-0.03 0 0.02"/>          <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.008 -0.03 0.01"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.008 0.03 0.01"/> +        <volume_morph            name="L_CLAVICLE"            scale="0.02 0.0 0.01"            pos="-0.02 0 0"/> @@ -4376,7 +4485,16 @@       value_default="0"       value_min="-3"       value_max="3"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.0 -0.01"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.0 -0.01"/> +	  </param_morph>      </param>      <param @@ -4389,7 +4507,16 @@       value_default="0"       value_min="-1.25"       value_max="1.25"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 -0.026 0.0"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.026 -0.0"/> +	  </param_morph>      </param>      <param @@ -4402,7 +4529,12 @@       value_default="0"       value_min="-1"       value_max="1"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="BELLY" +          scale="0.0 0.0 0.0" +          pos="0.0 0.0 0.05"/> +	  </param_morph>      </param>      <param @@ -4415,7 +4547,16 @@       value_default="0"       value_min="-2"       value_max="2"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="LEFT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.03 0.0"/> +        <volume_morph +          name="RIGHT_PEC" +          scale="0.0 0.0 0.0" +          pos="0.0 0.03 0.0"/> +	  </param_morph>      </param>      <!-- @@ -4518,6 +4659,10 @@            name="PELVIS"            scale="-0.01 0.0 0.0"            pos="0.01 0 0.0"/> +        <volume_morph +          name="BUTT" +          scale="0.0 0.0886 0.0" +          pos="0.03 0 0.0"/>        </param_morph>      </param> @@ -4949,7 +5094,11 @@       value_default="0"       value_min="-1"       value_max="1"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="BUTT" +          pos="0.0 0.0 0.05"/> +	  </param_morph>      </param>      <param @@ -4962,7 +5111,11 @@       value_default="0"       value_min="-1"       value_max="1"> -      <param_morph /> +      <param_morph> +        <volume_morph +          name="BUTT" +          pos="0.0 0.05 0.0"/> +	  </param_morph>      </param>      <!-- diff --git a/indra/newview/character/avatar_skeleton.xml b/indra/newview/character/avatar_skeleton.xml index 5e73804f2d..6b07bbc1d3 100755 --- a/indra/newview/character/avatar_skeleton.xml +++ b/indra/newview/character/avatar_skeleton.xml @@ -1,11 +1,18 @@  <?xml version="1.0" encoding="US-ASCII" standalone="yes"?> -<linden_skeleton version="1.0" num_bones="46" num_collision_volumes="19"> +<linden_skeleton version="1.0" num_bones="53" num_collision_volumes="26">  <bone name="mPelvis" pos="0.000 0.000 1.067" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 0.000000 1.067015">  	<collision_volume name="PELVIS" pos = "-0.01 0 -0.02" rot="0.000000 8.00000 0.000000" scale="0.12 0.16 0.17"/> +	<collision_volume name="BUTT" pos = "-0.06 0 -0.1" rot="0.000000 0.00000 0.000000" scale="0.1 0.1 0.1"/>  	<bone name="mTorso" pos="0.000 0.000 0.084" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 0.000000 0.084073">  		<collision_volume name="BELLY" pos = "0.028 0 0.04" rot="0.000000 8.00000 0.000000" scale="0.09 0.13 0.15"/> +		<collision_volume name="LOWER_BACK" pos = "0.0 0.0 0.023" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15"/> +		<collision_volume name="LEFT_HANDLE" pos = "0.0 0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05"/> +		<collision_volume name="RIGHT_HANDLE" pos = "0.0 -0.10 0.058" rot="0.000000 0.00000 0.000000" scale="0.05 0.05 0.05"/>  		<bone name="mChest" pos="-0.015 0.000 0.205" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="-0.015368 0.000000 0.204877">  			<collision_volume name="CHEST" pos = "0.028 0 0.07" rot="0.000000 -10.00000 0.000000" scale="0.11 0.15 0.2"/> +			<collision_volume name="UPPER_BACK" pos = "0.0 0.0 0.017" rot="0.000000 0.00000 0.000000" scale="0.09 0.13 0.15"/> +			<collision_volume name="LEFT_PEC" pos = "0.119 0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05"/> +			<collision_volume name="RIGHT_PEC" pos = "0.119 -0.082 0.042" rot="0.000000 4.29000 0.000000" scale="0.05 0.05 0.05"/>  			<bone name="mNeck" pos="-0.010 0.000 0.251" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="-0.009507 0.000000 0.251108">  				<collision_volume name="NECK" pos = "0.0 0 0.02" rot="0.000000 0.000000 0.000000" scale="0.05 0.06 0.08"/>  				<bone name="mHead" pos="0.000 -0.000 0.076" rot="0.000000 0.000000 0.000000" scale="1.000 1.000 1.000" pivot="0.000000 -0.000000 0.075630"> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 0a63358de2..54e0375a2b 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -260,11 +260,9 @@ bool handleSlowMotionAnimation(const LLSD& newvalue)  	return true;  } -// static -void LLAgent::parcelChangedCallback() +void LLAgent::setCanEditParcel() // called via mParcelChangedSignal  {  	bool can_edit = LLToolMgr::getInstance()->canEdit(); -  	gAgent.mCanEditParcel = can_edit;  } @@ -426,6 +424,8 @@ LLAgent::LLAgent() :  	mListener.reset(new LLAgentListener(*this)); +	addParcelChangedCallback(&setCanEditParcel); +  	mMoveTimer.stop();  } @@ -452,8 +452,6 @@ void LLAgent::init()  	mLastKnownRequestMaturity = mLastKnownResponseMaturity;  	mIsDoSendMaturityPreferenceToServer = true; -	LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&LLAgent::parcelChangedCallback)); -  	if (!mTeleportFinishedSlot.connected())  	{  		mTeleportFinishedSlot = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback(boost::bind(&LLAgent::handleTeleportFinished, this)); @@ -836,22 +834,33 @@ void LLAgent::handleServerBakeRegionTransition(const LLUUID& region_id)  	}  } +void LLAgent::changeParcels() +{ +	LL_DEBUGS("AgentLocation") << "Calling ParcelChanged callbacks" << LL_ENDL; +	// Notify anything that wants to know about parcel changes +	mParcelChangedSignal(); +} + +boost::signals2::connection LLAgent::addParcelChangedCallback(parcel_changed_callback_t cb) +{ +	return mParcelChangedSignal.connect(cb); +} +  //-----------------------------------------------------------------------------  // setRegion()  //-----------------------------------------------------------------------------  void LLAgent::setRegion(LLViewerRegion *regionp)  { -	bool teleport = true; - +	bool notifyRegionChange; +	  	llassert(regionp);  	if (mRegionp != regionp)  	{ -		// std::string host_name; -		// host_name = regionp->getHost().getHostName(); - +		notifyRegionChange = true; +		  		std::string ip = regionp->getHost().getString(); -		llinfos << "Moving agent into region: " << regionp->getName() -				<< " located at " << ip << llendl; +		LL_INFOS("AgentLocation") << "Moving agent into region: " << regionp->getName() +				<< " located at " << ip << LL_ENDL;  		if (mRegionp)  		{  			// We've changed regions, we're now going to change our agent coordinate frame. @@ -879,9 +888,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  			{  				gSky.mVOGroundp->setRegion(regionp);  			} - -			// Notify windlight managers -			teleport = (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE);  		}  		else  		{ @@ -903,8 +909,14 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  		// Pass new region along to metrics components that care about this level of detail.  		LLAppViewer::metricsUpdateRegion(regionp->getHandle());  	} +	else +	{ +		notifyRegionChange = false; +	}  	mRegionp = regionp; +	// TODO - most of what follows probably should be moved into callbacks +  	// Pass the region host to LLUrlEntryParcel to resolve parcel name  	// with a server request.  	LLUrlEntryParcel::setRegionHost(getRegionHost()); @@ -923,15 +935,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  	LLFloaterMove::sUpdateFlyingStatus(); -	if (teleport) -	{ -		LLEnvManagerNew::instance().onTeleport(); -	} -	else -	{ -		LLEnvManagerNew::instance().onRegionCrossing(); -	} -  	// If the newly entered region is using server bakes, and our  	// current appearance is non-baked, request appearance update from  	// server. @@ -944,6 +947,12 @@ void LLAgent::setRegion(LLViewerRegion *regionp)  		// Need to handle via callback after caps arrive.  		mRegionp->setCapabilitiesReceivedCallback(boost::bind(&LLAgent::handleServerBakeRegionTransition,this,_1));  	} + +	if (notifyRegionChange) +	{ +		LL_DEBUGS("AgentLocation") << "Calling RegionChanged callbacks" << LL_ENDL; +		mRegionChangedSignal(); +	}  } @@ -968,6 +977,16 @@ LLHost LLAgent::getRegionHost() const  	}  } +boost::signals2::connection LLAgent::addRegionChangedCallback(const region_changed_signal_t::slot_type& cb) +{ +	return mRegionChangedSignal.connect(cb); +} + +void LLAgent::removeRegionChangedCallback(boost::signals2::connection callback) +{ +	mRegionChangedSignal.disconnect(callback); +} +  //-----------------------------------------------------------------------------  // inPrelude()  //----------------------------------------------------------------------------- diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 7fac17d098..0766407494 100755 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -231,15 +231,54 @@ private:  	LLVector3		mHomePosRegion;  	//-------------------------------------------------------------------- -	// Region +	// Parcel  	//--------------------------------------------------------------------  public: +	void changeParcels(); // called by LLViewerParcelMgr when we cross a parcel boundary +	 +	// Register a boost callback to be called when the agent changes parcels +	typedef boost::function<void()> parcel_changed_callback_t; +	boost::signals2::connection     addParcelChangedCallback(parcel_changed_callback_t); + +private: +	typedef boost::signals2::signal<void()> parcel_changed_signal_t; +	parcel_changed_signal_t		mParcelChangedSignal; + +	//-------------------------------------------------------------------- +	// Region +	//-------------------------------------------------------------------- +  public:  	void			setRegion(LLViewerRegion *regionp);  	LLViewerRegion	*getRegion() const;  	LLHost			getRegionHost() const;  	BOOL			inPrelude(); -private: + +	/** +	 * Register a boost callback to be called when the agent changes regions +	 * Note that if you need to access a capability for the region, you may need to wait +	 * for the capabilities to be received, since in some cases your region changed +	 * callback will be called before the capabilities have been received.  Your callback +	 * may need to look something like: +	 * +	 * 	 LLViewerRegion* region = gAgent.getRegion(); +	 * 	 if (region->capabilitiesReceived()) +	 * 	 { +	 *       useCapability(region); +	 * 	 } +	 * 	 else // Need to handle via callback after caps arrive. +	 * 	 { +	 *       region->setCapabilitiesReceivedCallback(boost::bind(&useCapability,region,_1)); +	 *       // you may or may not want to remove that callback +	 * 	 } +	 */ +	typedef boost::signals2::signal<void()> region_changed_signal_t; + +	boost::signals2::connection     addRegionChangedCallback(const region_changed_signal_t::slot_type& cb); +	void                            removeRegionChangedCallback(boost::signals2::connection callback); + +  private:  	LLViewerRegion	*mRegionp; +	region_changed_signal_t		            mRegionChangedSignal;  	//--------------------------------------------------------------------  	// History @@ -640,9 +679,10 @@ private:  public:  	bool			canEditParcel() const { return mCanEditParcel; }  private: +	static void     setCanEditParcel();  	bool			mCanEditParcel; -	static void parcelChangedCallback(); +  /********************************************************************************   **                                                                            ** diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp index 0d7d41304d..70dcffefb2 100755 --- a/indra/newview/llappcorehttp.cpp +++ b/indra/newview/llappcorehttp.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -28,18 +28,81 @@  #include "llappcorehttp.h" +#include "llappviewer.h"  #include "llviewercontrol.h" +// Here is where we begin to get our connection usage under control. +// This establishes llcorehttp policy classes that, among other +// things, limit the maximum number of connections to outside +// services.  Each of the entries below maps to a policy class and +// has a limit, sometimes configurable, of how many connections can +// be open at a time. +  const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); +static const struct +{ +	LLAppCoreHttp::EAppPolicy	mPolicy; +	U32							mDefault; +	U32							mMin; +	U32							mMax; +	U32							mRate; +	std::string					mKey; +	const char *				mUsage; +} init_data[] =					//  Default and dynamic values for classes +{ +	{ +		LLAppCoreHttp::AP_DEFAULT,			8,		8,		8,		0, +		"", +		"other" +	}, +	{ +		LLAppCoreHttp::AP_TEXTURE,			8,		1,		12,		0, +		"TextureFetchConcurrency", +		"texture fetch" +	}, +	{ +		LLAppCoreHttp::AP_MESH1,			32,		1,		128,	100, +		"MeshMaxConcurrentRequests", +		"mesh fetch" +	}, +	{ +		LLAppCoreHttp::AP_MESH2,			8,		1,		32,		100, +		"Mesh2MaxConcurrentRequests", +		"mesh2 fetch" +	}, +	{ +		LLAppCoreHttp::AP_LARGE_MESH,		2,		1,		8,		0, +		"", +		"large mesh fetch" +	}, +	{ +		LLAppCoreHttp::AP_UPLOADS,			2,		1,		8,		0, +		"", +		"asset upload" +	}, +	{ +		LLAppCoreHttp::AP_LONG_POLL,		32,		32,		32,		0, +		"", +		"long poll" +	} +}; + +static void setting_changed(); +  LLAppCoreHttp::LLAppCoreHttp()  	: mRequest(NULL),  	  mStopHandle(LLCORE_HTTP_HANDLE_INVALID),  	  mStopRequested(0.0), -	  mStopped(false), -	  mPolicyDefault(-1) -{} +	  mStopped(false) +{ +	for (int i(0); i < LL_ARRAY_SIZE(mPolicies); ++i) +	{ +		mPolicies[i] = LLCore::HttpRequest::DEFAULT_POLICY_ID; +		mSettings[i] = 0U; +	} +}  LLAppCoreHttp::~LLAppCoreHttp() @@ -54,30 +117,28 @@ void LLAppCoreHttp::init()  	LLCore::HttpStatus status = LLCore::HttpRequest::createService();  	if (! status)  	{ -		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  " -						<< status.toString() +		LL_ERRS("Init") << "Failed to initialize HTTP services.  Reason:  " << status.toString()  						<< LL_ENDL;  	}  	// Point to our certs or SSH/https: will fail on connect -	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, -														gDirUtilp->getCAFile()); +	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_CA_FILE, +														LLCore::HttpRequest::GLOBAL_POLICY_ID, +														gDirUtilp->getCAFile(), NULL);  	if (! status)  	{ -		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  " -						<< status.toString() +		LL_ERRS("Init") << "Failed to set CA File for HTTP services.  Reason:  " << status.toString()  						<< LL_ENDL;  	} -	// Establish HTTP Proxy.  "LLProxy" is a special string which directs -	// the code to use LLProxy::applyProxySettings() to establish any -	// HTTP or SOCKS proxy for http operations. -	status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); +	// Establish HTTP Proxy, if desired. +	status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_LLPROXY, +														LLCore::HttpRequest::GLOBAL_POLICY_ID, +														1, NULL);  	if (! status)  	{ -		LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " -						<< status.toString() -						<< LL_ENDL; +		LL_WARNS("Init") << "Failed to set HTTP proxy for HTTP services.  Reason:  " << status.toString() +						 << LL_ENDL;  	}  	// Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): @@ -90,47 +151,74 @@ void LLAppCoreHttp::init()  	{  		long trace_level(0L);  		trace_level = long(gSavedSettings.getU32(http_trace)); -		status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); +		status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_TRACE, +															LLCore::HttpRequest::GLOBAL_POLICY_ID, +															trace_level, NULL);  	}  	// Setup default policy and constrain if directed to -	mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; -	static const std::string texture_concur("TextureFetchConcurrency"); -	if (gSavedSettings.controlExists(texture_concur)) +	mPolicies[AP_DEFAULT] = LLCore::HttpRequest::DEFAULT_POLICY_ID; + +	// Setup additional policies based on table and some special rules +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i)  	{ -		U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); +		const EAppPolicy policy(init_data[i].mPolicy); -		if (concur > 0) +		if (AP_DEFAULT == policy)  		{ -			LLCore::HttpStatus status; -			status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, -															   LLCore::HttpRequest::CP_CONNECTION_LIMIT, -															   concur); -			if (! status) -			{ -				LL_WARNS("Init") << "Unable to set texture fetch concurrency.  Reason:  " -								 << status.toString() -								 << LL_ENDL; -			} -			else -			{ -				LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency.  New value:  " -								 << concur -								 << LL_ENDL; -			} +			// Pre-created +			continue; +		} + +		mPolicies[policy] = LLCore::HttpRequest::createPolicyClass(); +		if (! mPolicies[policy]) +		{ +			// Use default policy (but don't accidentally modify default) +			LL_WARNS("Init") << "Failed to create HTTP policy class for " << init_data[i].mUsage +							 << ".  Using default policy." +							 << LL_ENDL; +			mPolicies[policy] = mPolicies[AP_DEFAULT]; +			continue;  		}  	} + +	// Need a request object to handle dynamic options before setting them +	mRequest = new LLCore::HttpRequest; + +	// Apply initial settings +	refreshSettings(true);  	// Kick the thread  	status = LLCore::HttpRequest::startThread();  	if (! status)  	{ -		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  " -						<< status.toString() +		LL_ERRS("Init") << "Failed to start HTTP servicing thread.  Reason:  " << status.toString()  						<< LL_ENDL;  	} -	mRequest = new LLCore::HttpRequest; +	// Register signals for settings and state changes +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) +	{ +		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) +		{ +			LLPointer<LLControlVariable> cntrl_ptr = gSavedSettings.getControl(init_data[i].mKey); +			if (cntrl_ptr.isNull()) +			{ +				LL_WARNS("Init") << "Unable to set signal on global setting '" << init_data[i].mKey +								 << "'" << LL_ENDL; +			} +			else +			{ +				mSettingsSignal[i] = cntrl_ptr->getCommitSignal()->connect(boost::bind(&setting_changed)); +			} +		} +	} +} + + +void setting_changed() +{ +	LLAppViewer::instance()->getAppCoreHttp().refreshSettings(false);  } @@ -173,6 +261,11 @@ void LLAppCoreHttp::cleanup()  		}  	} +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) +	{ +		mSettingsSignal[i].disconnect(); +	} +	  	delete mRequest;  	mRequest = NULL; @@ -185,6 +278,78 @@ void LLAppCoreHttp::cleanup()  	}  } +void LLAppCoreHttp::refreshSettings(bool initial) +{ +	LLCore::HttpStatus status; +	 +	for (int i(0); i < LL_ARRAY_SIZE(init_data); ++i) +	{ +		const EAppPolicy policy(init_data[i].mPolicy); + +		// Set any desired throttle +		if (initial && init_data[i].mRate) +		{ +			// Init-time only, can use the static setters here +			status = LLCore::HttpRequest::setStaticPolicyOption(LLCore::HttpRequest::PO_THROTTLE_RATE, +																mPolicies[policy], +																init_data[i].mRate, +																NULL); +			if (! status) +			{ +				LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +								 << " throttle rate.  Reason:  " << status.toString() +								 << LL_ENDL; +			} +		} + +		// Get target connection concurrency value +		U32 setting(init_data[i].mDefault); +		if (! init_data[i].mKey.empty() && gSavedSettings.controlExists(init_data[i].mKey)) +		{ +			U32 new_setting(gSavedSettings.getU32(init_data[i].mKey)); +			if (new_setting) +			{ +				// Treat zero settings as an ask for default +				setting = llclamp(new_setting, init_data[i].mMin, init_data[i].mMax); +			} +		} + +		if (! initial && setting == mSettings[policy]) +		{ +			// Unchanged, try next setting +			continue; +		} +		 +		// Set it and report +		// *TODO:  These are intended to be per-host limits when we can +		// support that in llcorehttp/libcurl. +		LLCore::HttpHandle handle; +		handle = mRequest->setPolicyOption(LLCore::HttpRequest::PO_CONNECTION_LIMIT, +										   mPolicies[policy], +										   setting, NULL); +		if (LLCORE_HTTP_HANDLE_INVALID == handle) +		{ +			status = mRequest->getStatus(); +			LL_WARNS("Init") << "Unable to set " << init_data[i].mUsage +							 << " concurrency.  Reason:  " << status.toString() +							 << LL_ENDL; +		} +		else +		{ +			LL_DEBUGS("Init") << "Changed " << init_data[i].mUsage +							  << " concurrency.  New value:  " << setting +							  << LL_ENDL; +			mSettings[policy] = setting; +			if (initial && setting != init_data[i].mDefault) +			{ +				LL_INFOS("Init") << "Application settings overriding default " << init_data[i].mUsage +								 << " concurrency.  New value:  " << setting +								 << LL_ENDL; +			} +		} +	} +} +  void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *)  { diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h index 241d73ad52..40e3042b84 100755 --- a/indra/newview/llappcorehttp.h +++ b/indra/newview/llappcorehttp.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2012&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -41,6 +41,117 @@  class LLAppCoreHttp : public LLCore::HttpHandler  {  public: +	typedef LLCore::HttpRequest::policy_t policy_t; + +	enum EAppPolicy +	{ +		/// Catchall policy class.  Not used yet +		/// but will have a generous concurrency +		/// limit.  Deep queueing possible by having +		/// a chatty HTTP user. +		/// +		/// Destination:     anywhere +		/// Protocol:        http: or https: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     high  +		/// Request rate:    unknown +		/// Pipelined:       no +		AP_DEFAULT, + +		/// Texture fetching policy class.  Used to +		/// download textures via capability or SSA +		/// baking service.  Deep queueing of requests. +		/// Do not share. +		/// +		/// Destination:     simhost:12046 & bake-texture:80 +		/// Protocol:        http: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     high +		/// Request rate:    high +		/// Pipelined:       soon +		AP_TEXTURE, + +		/// Legacy mesh fetching policy class.  Used to +		/// download textures via 'GetMesh' capability. +		/// To be deprecated.  Do not share. +		/// +		/// Destination:     simhost:12046 +		/// Protocol:        http: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     dangerously high +		/// Request rate:    high +		/// Pipelined:       no +		AP_MESH1, + +		/// New mesh fetching policy class.  Used to +		/// download textures via 'GetMesh2' capability. +		/// Used when fetch request (typically one LOD) +		/// is 'small', currently defined as 2MB. +		/// Very deeply queued.  Do not share. +		/// +		/// Destination:     simhost:12046 +		/// Protocol:        http: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     high +		/// Request rate:    high +		/// Pipelined:       soon +		AP_MESH2, + +		/// Large mesh fetching policy class.  Used to +		/// download textures via 'GetMesh' or 'GetMesh2' +		/// capability.  Used when fetch request +		/// is not small to avoid head-of-line problem +		/// when large requests block a sequence of small, +		/// fast requests.  Can be shared with similar +		/// traffic that can wait for longish stalls +		/// (default timeout 600S). +		/// +		/// Destination:     simhost:12046 +		/// Protocol:        http: +		/// Transfer size:   MB +		/// Long poll:       no +		/// Concurrency:     low +		/// Request rate:    low +		/// Pipelined:       soon +		AP_LARGE_MESH, + +		/// Asset upload policy class.  Used to store +		/// assets (mesh only at the moment) via +		/// changeable URL.  Responses may take some +		/// time (default timeout 240S). +		/// +		/// Destination:     simhost:12043 +		/// Protocol:        https: +		/// Transfer size:   KB-MB +		/// Long poll:       no +		/// Concurrency:     low +		/// Request rate:    low +		/// Pipelined:       no +		AP_UPLOADS, + +		/// Long-poll-type HTTP requests.  Not +		/// bound by a connection limit.  Requests +		/// will typically hang around for a long +		/// time (~30S).  Only shareable with other +		/// long-poll requests. +		/// +		/// Destination:     simhost:12043 +		/// Protocol:        https: +		/// Transfer size:   KB +		/// Long poll:       yes +		/// Concurrency:     unlimited but low in practice +		/// Request rate:    low +		/// Pipelined:       no +		AP_LONG_POLL, + +		AP_COUNT						// Must be last +	}; +	 +public:  	LLAppCoreHttp();  	~LLAppCoreHttp(); @@ -65,21 +176,27 @@ public:  	// Notification when the stop request is complete.  	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); -	// Retrieve the policy class for default operations. -	int getPolicyDefault() const +	// Retrieve a policy class identifier for desired +	// application function. +	policy_t getPolicy(EAppPolicy policy) const  		{ -			return mPolicyDefault; +			return mPolicies[policy];  		} + +	// Apply initial or new settings from the environment. +	void refreshSettings(bool initial);  private:  	static const F64			MAX_THREAD_WAIT_TIME;  private: -	LLCore::HttpRequest *		mRequest; +	LLCore::HttpRequest *		mRequest;						// Request queue to issue shutdowns  	LLCore::HttpHandle			mStopHandle;  	F64							mStopRequested;  	bool						mStopped; -	int							mPolicyDefault; +	policy_t					mPolicies[AP_COUNT];			// Policy class id for each connection set +	U32							mSettings[AP_COUNT]; +	boost::signals2::connection mSettingsSignal[AP_COUNT];		// Signals to global settings that affect us  }; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index f622d5a63a..24f467f954 100755 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -55,6 +55,7 @@ static U32 sDataMask = LLDrawPoolAvatar::VERTEX_DATA_MASK;  static U32 sBufferUsage = GL_STREAM_DRAW_ARB;  static U32 sShaderLevel = 0; +#define JOINT_COUNT 52  LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL;  BOOL	LLDrawPoolAvatar::sSkipOpaque = FALSE; @@ -1582,10 +1583,11 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*  		LLVector4a* norm = has_normal ? (LLVector4a*) normal.get() : NULL;  		//build matrix palette -		LLMatrix4a mp[64]; +		LLMatrix4a mp[JOINT_COUNT];  		LLMatrix4* mat = (LLMatrix4*) mp; -		for (U32 j = 0; j < skin->mJointNames.size(); ++j) +		U32 count = llmin((U32) skin->mJointNames.size(), (U32) JOINT_COUNT); +		for (U32 j = 0; j < count; ++j)  		{  			LLJoint* joint = avatar->getJoint(skin->mJointNames[j]);  			if (joint) @@ -1642,6 +1644,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLVOAvatar* avatar, LLFace*  				LLVector4a& n = vol_face.mNormals[j];  				bind_shape_matrix.rotate(n, t);  				final_mat.rotate(t, dst); +				dst.normalize3fast();  				norm[j] = dst;  			}  		} @@ -1708,9 +1711,9 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)  		{  			if (sShaderLevel > 0)  			{ //upload matrix palette to shader -				LLMatrix4 mat[32]; +				LLMatrix4 mat[JOINT_COUNT]; -				U32 count = llmin((U32) skin->mJointNames.size(), (U32) 32); +				U32 count = llmin((U32) skin->mJointNames.size(), (U32) JOINT_COUNT);  				for (U32 i = 0; i < count; ++i)  				{ @@ -1724,10 +1727,42 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow)  				stop_glerror(); -				LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv(LLViewerShaderMgr::AVATAR_MATRIX,  +				F32 mp[JOINT_COUNT*9]; + +				F32 transp[JOINT_COUNT*3]; + +				for (U32 i = 0; i < count; ++i) +				{ +					F32* m = (F32*) mat[i].mMatrix; + +					U32 idx = i*9; + +					mp[idx+0] = m[0]; +					mp[idx+1] = m[1]; +					mp[idx+2] = m[2]; + +					mp[idx+3] = m[4]; +					mp[idx+4] = m[5]; +					mp[idx+5] = m[6]; + +					mp[idx+6] = m[8]; +					mp[idx+7] = m[9]; +					mp[idx+8] = m[10]; + +					idx = i*3; + +					transp[idx+0] = m[12]; +					transp[idx+1] = m[13]; +					transp[idx+2] = m[14]; +				} + +				LLDrawPoolAvatar::sVertexProgram->uniformMatrix3fv(LLViewerShaderMgr::AVATAR_MATRIX,   					count,  					FALSE, -					(GLfloat*) mat[0].mMatrix); +					(GLfloat*) mp); + +				LLDrawPoolAvatar::sVertexProgram->uniform3fv(LLShaderMgr::AVATAR_TRANSLATION, count, transp); +  				stop_glerror();  			} diff --git a/indra/newview/llenvmanager.cpp b/indra/newview/llenvmanager.cpp index 86fe6754dc..589cf28615 100755 --- a/indra/newview/llenvmanager.cpp +++ b/indra/newview/llenvmanager.cpp @@ -92,9 +92,11 @@ void LLEnvPrefs::setUseDayCycle(const std::string& name)  }  //============================================================================= -LLEnvManagerNew::LLEnvManagerNew() +LLEnvManagerNew::LLEnvManagerNew(): +	mInterpNextChangeMessage(true), +	mCurRegionUUID(LLUUID::null), +	mLastReceivedID(LLUUID::null)  { -	mInterpNextChangeMessage = true;  	// Set default environment settings.  	mUserPrefs.mUseRegionSettings = true; @@ -102,6 +104,9 @@ LLEnvManagerNew::LLEnvManagerNew()  	mUserPrefs.mWaterPresetName = "Default";  	mUserPrefs.mSkyPresetName = "Default";  	mUserPrefs.mDayCycleName = "Default"; + +	LL_DEBUGS("Windlight")<<LL_ENDL; +	gAgent.addRegionChangedCallback(boost::bind(&LLEnvManagerNew::onRegionChange, this));  }  bool LLEnvManagerNew::getUseRegionSettings() const @@ -300,6 +305,11 @@ void LLEnvManagerNew::loadUserPrefs()  	mUserPrefs.mUseRegionSettings	= gSavedSettings.getBOOL("UseEnvironmentFromRegion");  	mUserPrefs.mUseDayCycle			= gSavedSettings.getBOOL("UseDayCycle"); + +	if (mUserPrefs.mUseRegionSettings) +	{ +		requestRegionSettings(); +	}  }  void LLEnvManagerNew::saveUserPrefs() @@ -398,6 +408,7 @@ void LLEnvManagerNew::dumpPresets()  void LLEnvManagerNew::requestRegionSettings()  { +	LL_DEBUGS("Windlight") << LL_ENDL;  	LLEnvironmentRequest::initiate();  } @@ -422,11 +433,6 @@ boost::signals2::connection LLEnvManagerNew::setRegionSettingsChangeCallback(con  	return mRegionSettingsChangeSignal.connect(cb);  } -boost::signals2::connection LLEnvManagerNew::setRegionChangeCallback(const region_change_signal_t::slot_type& cb) -{ -	return mRegionChangeSignal.connect(cb); -} -  boost::signals2::connection LLEnvManagerNew::setRegionSettingsAppliedCallback(const region_settings_applied_signal_t::slot_type& cb)  {  	return mRegionSettingsAppliedSignal.connect(cb); @@ -457,25 +463,13 @@ const std::string LLEnvManagerNew::getScopeString(LLEnvKey::EScope scope)  	}  } -void LLEnvManagerNew::onRegionCrossing() -{ -	LL_DEBUGS("Windlight") << "Crossed region" << LL_ENDL; -	onRegionChange(true); -} - -void LLEnvManagerNew::onTeleport() -{ -	LL_DEBUGS("Windlight") << "Teleported" << LL_ENDL; -	onRegionChange(false); -} -  void LLEnvManagerNew::onRegionSettingsResponse(const LLSD& content)  {  	// If the message was valid, grab the UUID from it and save it for next outbound update message.  	mLastReceivedID = content[0]["messageID"].asUUID();  	// Refresh cached region settings. -	LL_DEBUGS("Windlight") << "Caching region environment settings: " << content << LL_ENDL; +	LL_DEBUGS("Windlight") << "Received region environment settings: " << content << LL_ENDL;  	F32 sun_hour = 0; // *TODO  	LLEnvironmentSettings new_settings(content[1], content[2], content[3], sun_hour);  	mCachedRegionPrefs = new_settings; @@ -594,6 +588,7 @@ void LLEnvManagerNew::updateWaterFromPrefs(bool interpolate)  void LLEnvManagerNew::updateManagersFromPrefs(bool interpolate)  { +	LL_DEBUGS("Windlight")<<LL_ENDL;  	// Apply water settings.  	updateWaterFromPrefs(interpolate); @@ -651,28 +646,35 @@ bool LLEnvManagerNew::useDefaultWater()  } -void LLEnvManagerNew::onRegionChange(bool interpolate) +void LLEnvManagerNew::onRegionChange()  {  	// Avoid duplicating region setting requests  	// by checking whether the region is actually changing.  	LLViewerRegion* regionp = gAgent.getRegion();  	LLUUID region_uuid = regionp ? regionp->getRegionID() : LLUUID::null; -	if (region_uuid == mCurRegionUUID) +	if (region_uuid != mCurRegionUUID)  	{ -		return; +		// Clear locally modified region settings. +		mNewRegionPrefs.clear(); + +		// *TODO: clear environment settings of the previous region? + +		// Request environment settings of the new region. +		mCurRegionUUID = region_uuid; +		// for region crossings, interpolate the change; for teleports, don't +		mInterpNextChangeMessage = (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE); +		LL_DEBUGS("Windlight") << (mInterpNextChangeMessage ? "Crossed" : "Teleported") +							   << " to new region: " << region_uuid +							   << LL_ENDL; +		requestRegionSettings(); +	} +	else +	{ +		LL_DEBUGS("Windlight") << "disregarding region change; interp: " +							   << (mInterpNextChangeMessage ? "true" : "false") +							   << " regionp: " << regionp +							   << " old: " << mCurRegionUUID +							   << " new: " << region_uuid +							   << LL_ENDL;  	} - -	// Clear locally modified region settings. -	mNewRegionPrefs.clear(); - -	// *TODO: clear environment settings of the previous region? - -	// Request environment settings of the new region. -	LL_DEBUGS("Windlight") << "New viewer region: " << region_uuid << LL_ENDL; -	mCurRegionUUID = region_uuid; -	mInterpNextChangeMessage = interpolate; -	requestRegionSettings(); - -	// Let interested parties know agent region has been changed. -	mRegionChangeSignal();  } diff --git a/indra/newview/llenvmanager.h b/indra/newview/llenvmanager.h index ad56761bc7..c7877303fc 100755 --- a/indra/newview/llenvmanager.h +++ b/indra/newview/llenvmanager.h @@ -166,7 +166,6 @@ class LLEnvManagerNew : public LLSingleton<LLEnvManagerNew>  public:  	typedef boost::signals2::signal<void()> prefs_change_signal_t;  	typedef boost::signals2::signal<void()> region_settings_change_signal_t; -	typedef boost::signals2::signal<void()> region_change_signal_t;  	typedef boost::signals2::signal<void(bool)> region_settings_applied_signal_t;  	LLEnvManagerNew(); @@ -222,15 +221,12 @@ public:  	bool sendRegionSettings(const LLEnvironmentSettings& new_settings);  	boost::signals2::connection setPreferencesChangeCallback(const prefs_change_signal_t::slot_type& cb);  	boost::signals2::connection setRegionSettingsChangeCallback(const region_settings_change_signal_t::slot_type& cb); -	boost::signals2::connection setRegionChangeCallback(const region_change_signal_t::slot_type& cb);  	boost::signals2::connection setRegionSettingsAppliedCallback(const region_settings_applied_signal_t::slot_type& cb);  	static bool canEditRegionSettings(); /// @return true if we have access to editing region environment  	static const std::string getScopeString(LLEnvKey::EScope scope);  	// Public callbacks. -	void onRegionCrossing(); -	void onTeleport();  	void onRegionSettingsResponse(const LLSD& content);  	void onRegionSettingsApplyResponse(bool ok); @@ -251,7 +247,7 @@ private:  	bool useDefaultSky();  	bool useDefaultWater(); -	void onRegionChange(bool interpolate); +	void onRegionChange();  	/// Emitted when user environment preferences change.  	prefs_change_signal_t mUsePrefsChangeSignal; @@ -260,9 +256,6 @@ private:  	region_settings_change_signal_t	mRegionSettingsChangeSignal;  	/// Emitted when agent region changes. Move to LLAgent? -	region_change_signal_t	mRegionChangeSignal; - -	/// Emitted when agent region changes. Move to LLAgent?  	region_settings_applied_signal_t mRegionSettingsAppliedSignal;  	LLEnvPrefs				mUserPrefs;					/// User environment preferences. diff --git a/indra/newview/llfloatereditdaycycle.cpp b/indra/newview/llfloatereditdaycycle.cpp index b63677b258..78e20e3bf0 100755 --- a/indra/newview/llfloatereditdaycycle.cpp +++ b/indra/newview/llfloatereditdaycycle.cpp @@ -145,7 +145,7 @@ void LLFloaterEditDayCycle::initCallbacks(void)  	// Connect to env manager events.  	LLEnvManagerNew& env_mgr = LLEnvManagerNew::instance();  	env_mgr.setRegionSettingsChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsChange, this)); -	env_mgr.setRegionChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionChange, this)); +	gAgent.addRegionChangedCallback(boost::bind(&LLFloaterEditDayCycle::onRegionChange, this));  	env_mgr.setRegionSettingsAppliedCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsApplied, this, _1));  	// Connect to day cycle manager events. diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp index 14e1a486d3..84921849d0 100755 --- a/indra/newview/llfloaterimsession.cpp +++ b/indra/newview/llfloaterimsession.cpp @@ -61,6 +61,9 @@  #include "llnotificationmanager.h"  #include "llautoreplace.h" +const F32 ME_TYPING_TIMEOUT = 4.0f; +const F32 OTHER_TYPING_TIMEOUT = 9.0f; +  floater_showed_signal_t LLFloaterIMSession::sIMFloaterShowedSignal;  LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id) @@ -75,7 +78,10 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)  	mTypingTimer(),  	mTypingTimeoutTimer(),  	mPositioned(false), -	mSessionInitialized(false) +	mSessionInitialized(false), +	mMeTypingTimer(), +	mOtherTypingTimer(), +	mImInfo()  {  	mIsNearbyChat = false; @@ -96,13 +102,31 @@ LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)  void LLFloaterIMSession::refresh()  {  	if (mMeTyping) -{ +	{ +		// Send an additional Start Typing packet every ME_TYPING_TIMEOUT seconds +		if (mMeTypingTimer.getElapsedTimeF32() > ME_TYPING_TIMEOUT && false == mShouldSendTypingState) +		{ +			LL_DEBUGS("TypingMsgs") << "Send additional Start Typing packet" << LL_ENDL; +			LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); +			mMeTypingTimer.reset(); +		} +  		// Time out if user hasn't typed for a while.  		if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)  		{ -	setTyping(false); +			setTyping(false); +			LL_DEBUGS("TypingMsgs") << "Send stop typing due to timeout" << LL_ENDL;  		}  	} + +	// Clear <name is typing> message if no data received for OTHER_TYPING_TIMEOUT seconds +	if (mOtherTyping && mOtherTypingTimer.getElapsedTimeF32() > OTHER_TYPING_TIMEOUT) +	{ +		LL_DEBUGS("TypingMsgs") << "Received: is typing cleared due to timeout" << LL_ENDL; +		removeTypingIndicator(mImInfo); +		mOtherTyping = false; +	} +  }  // virtual @@ -953,13 +977,21 @@ void LLFloaterIMSession::setTyping(bool typing)  	// much network traffic. Only send in person-to-person IMs.  	if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL )  	{ -		// Still typing, send 'start typing' notification or -		// send 'stop typing' notification immediately -		if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) +		if ( mMeTyping )  		{ -			LLIMModel::instance().sendTypingState(mSessionID, -					mOtherParticipantUUID, mMeTyping); -					mShouldSendTypingState = false; +			if ( mTypingTimer.getElapsedTimeF32() > 1.f ) +			{ +				// Still typing, send 'start typing' notification +				LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE); +				mShouldSendTypingState = false; +				mMeTypingTimer.reset(); +			} +		} +		else +		{ +			// Send 'stop typing' notification immediately +			LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE); +			mShouldSendTypingState = false;  		}  	} @@ -975,10 +1007,12 @@ void LLFloaterIMSession::setTyping(bool typing)  void LLFloaterIMSession::processIMTyping(const LLIMInfo* im_info, BOOL typing)  { +	LL_DEBUGS("TypingMsgs") << "typing=" << typing << LL_ENDL;  	if ( typing )  	{  		// other user started typing  		addTypingIndicator(im_info); +		mOtherTypingTimer.reset();  	}  	else  	{ @@ -1202,10 +1236,40 @@ BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids)  void LLFloaterIMSession::addTypingIndicator(const LLIMInfo* im_info)  { +/* Operation of "<name> is typing" state machine: +Not Typing state: + +    User types in P2P IM chat ... Send Start Typing, save Started time, +    start Idle Timer (N seconds) go to Typing state + +Typing State: + +    User enters a non-return character: if Now - Started > ME_TYPING_TIMEOUT, send +    Start Typing, restart Idle Timer +    User enters a return character: stop Idle Timer, send IM and Stop +    Typing, go to Not Typing state +    Idle Timer expires: send Stop Typing, go to Not Typing state + +The recipient has a complementary state machine in which a Start Typing +that is not followed by either an IM or another Start Typing within OTHER_TYPING_TIMEOUT +seconds switches the sender out of typing state. + +This has the nice quality of being self-healing for lost start/stop +messages while adding messages only for the (relatively rare) case of a +user who types a very long message (one that takes more than ME_TYPING_TIMEOUT seconds +to type). + +Note: OTHER_TYPING_TIMEOUT must be > ME_TYPING_TIMEOUT for proper operation of the state machine + +*/ +  	// We may have lost a "stop-typing" packet, don't add it twice  	if (im_info && !mOtherTyping)  	{  		mOtherTyping = true; +		mOtherTypingTimer.reset(); +		// Save im_info so that removeTypingIndicator can be properly called because a timeout has occurred +		mImInfo = im_info;  		// Update speaker  		LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); diff --git a/indra/newview/llfloaterimsession.h b/indra/newview/llfloaterimsession.h index d6718843ca..2b9d06e744 100755 --- a/indra/newview/llfloaterimsession.h +++ b/indra/newview/llfloaterimsession.h @@ -187,6 +187,8 @@ private:  	LLFrameTimer mTypingTimer;  	LLFrameTimer mTypingTimeoutTimer;  	bool mSessionNameUpdatedForTyping; +	LLFrameTimer mMeTypingTimer; +	LLFrameTimer mOtherTypingTimer;  	bool mSessionInitialized;  	LLSD mQueuedMsgsForInit; @@ -196,6 +198,8 @@ private:  	// connection to voice channel state change signal  	boost::signals2::connection mVoiceChannelStateChangeConnection; + +	const LLIMInfo* mImInfo;  };  #endif  // LL_FLOATERIMSESSION_H diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 6c8e81e563..b16ef6dd79 100755 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1791,10 +1791,15 @@ void LLPanelLandObjects::onCommitClean(LLUICtrl *caller, void* user_data)  	LLParcel* parcel = lop->mParcel->getParcel();  	if (parcel)  	{ -		lop->mOtherTime = atoi(lop->mCleanOtherObjectsTime->getText().c_str()); +		S32 return_time = atoi(lop->mCleanOtherObjectsTime->getText().c_str()); +		// Only send return time if it has changed +		if (return_time != lop->mOtherTime) +		{ +			lop->mOtherTime = return_time; -		parcel->setCleanOtherTime(lop->mOtherTime); -		send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime); +			parcel->setCleanOtherTime(lop->mOtherTime); +			send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime); +		}  	}  } diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 19cec55837..855836af7a 100755 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -535,9 +535,16 @@ BOOL LLFloaterModelPreview::postBuild()  	mUploadBtn = getChild<LLButton>("ok_btn");  	mCalculateBtn = getChild<LLButton>("calculate_btn"); -	mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this)); +	if (LLConvexDecomposition::getInstance() != NULL) +	{ +		mCalculateBtn->setClickedCallback(boost::bind(&LLFloaterModelPreview::onClickCalculateBtn, this)); -	toggleCalculateButton(true); +		toggleCalculateButton(true); +	} +	else +	{ +		mCalculateBtn->setEnabled(false); +	}  	return TRUE;  } diff --git a/indra/newview/llfloaterpathfindingconsole.cpp b/indra/newview/llfloaterpathfindingconsole.cpp index 298454724b..161259d049 100755 --- a/indra/newview/llfloaterpathfindingconsole.cpp +++ b/indra/newview/llfloaterpathfindingconsole.cpp @@ -34,11 +34,11 @@  #include <boost/signals2.hpp> +#include "llagent.h"  #include "llbutton.h"  #include "llcheckboxctrl.h"  #include "llcombobox.h"  #include "llcontrol.h" -#include "llenvmanager.h"  #include "llfloaterpathfindingcharacters.h"  #include "llfloaterpathfindinglinksets.h"  #include "llfloaterreg.h" @@ -224,7 +224,7 @@ void LLFloaterPathfindingConsole::onOpen(const LLSD& pKey)  	if (!mRegionBoundarySlot.connected())  	{ -		mRegionBoundarySlot = LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLFloaterPathfindingConsole::onRegionBoundaryCross, this)); +		mRegionBoundarySlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterPathfindingConsole::onRegionBoundaryCross, this));  	}  	if (!mTeleportFailedSlot.connected()) diff --git a/indra/newview/llfloaterpathfindingobjects.cpp b/indra/newview/llfloaterpathfindingobjects.cpp index 20c1215bcb..d72ee073e1 100755 --- a/indra/newview/llfloaterpathfindingobjects.cpp +++ b/indra/newview/llfloaterpathfindingobjects.cpp @@ -41,7 +41,6 @@  #include "llavatarnamecache.h"  #include "llbutton.h"  #include "llcheckboxctrl.h" -#include "llenvmanager.h"  #include "llfloater.h"  #include "llfontgl.h"  #include "llnotifications.h" @@ -85,7 +84,7 @@ void LLFloaterPathfindingObjects::onOpen(const LLSD &pKey)  	if (!mRegionBoundaryCrossingSlot.connected())  	{ -		mRegionBoundaryCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLFloaterPathfindingObjects::onRegionBoundaryCrossed, this)); +		mRegionBoundaryCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterPathfindingObjects::onRegionBoundaryCrossed, this));  	}  	if (!mGodLevelChangeSlot.connected()) diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 66bf49331b..73c0963a1d 100755 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -91,6 +91,7 @@  #include "lltrans.h"  #include "llagentui.h"  #include "llmeshrepository.h" +#include "llfloaterregionrestarting.h"  const S32 TERRAIN_TEXTURE_COUNT = 4;  const S32 CORNER_COUNT = 4; @@ -219,7 +220,7 @@ BOOL LLFloaterRegionInfo::postBuild()  		&processEstateOwnerRequest);  	// Request region info when agent region changes. -	LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLFloaterRegionInfo::requestRegionInfo, this)); +	gAgent.addRegionChangedCallback(boost::bind(&LLFloaterRegionInfo::requestRegionInfo, this));  	return TRUE;  } diff --git a/indra/newview/llfloaterregionrestarting.cpp b/indra/newview/llfloaterregionrestarting.cpp new file mode 100644 index 0000000000..95d4265bb4 --- /dev/null +++ b/indra/newview/llfloaterregionrestarting.cpp @@ -0,0 +1,176 @@ +/**  + * @file llfloaterregionrestarting.cpp + * @brief Shows countdown timer during region restart + * + * $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 "llfloaterregionrestarting.h" + +#include "llfloaterreg.h" +#include "lluictrl.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llviewerwindow.h" + +static S32 sSeconds; +static U32 sShakeState; + +LLFloaterRegionRestarting::LLFloaterRegionRestarting(const LLSD& key) : +	LLFloater(key), +	LLEventTimer(1) +{ +	mName = (std::string)key["NAME"]; +	sSeconds = (LLSD::Integer)key["SECONDS"]; +} + +LLFloaterRegionRestarting::~LLFloaterRegionRestarting() +{ +	mRegionChangedConnection.disconnect(); +} + +BOOL LLFloaterRegionRestarting::postBuild() +{ +	mRegionChangedConnection = gAgent.addRegionChangedCallback(boost::bind(&LLFloaterRegionRestarting::regionChange, this)); + +	LLStringUtil::format_map_t args; +	std::string text; + +	args["[NAME]"] = mName; +	text = getString("RegionName", args); +	LLTextBox* textbox = getChild<LLTextBox>("region_name"); +	textbox->setValue(text); + +	sShakeState = SHAKE_START; + +	refresh(); + +	return TRUE; +} + +void LLFloaterRegionRestarting::regionChange() +{ +	close(); +} + +BOOL LLFloaterRegionRestarting::tick() +{ +	refresh(); + +	return FALSE; +} + +void LLFloaterRegionRestarting::refresh() +{ +	LLStringUtil::format_map_t args; +	std::string text; + +	args["[SECONDS]"] = llformat("%d", sSeconds); +	getChild<LLTextBox>("restart_seconds")->setValue(getString("RestartSeconds", args)); + +	sSeconds = sSeconds - 1; +	if(sSeconds < 0.0) +	{ +		sSeconds = 0; +	} +} + +void LLFloaterRegionRestarting::draw() +{ +	LLFloater::draw(); + +	const F32 SHAKE_INTERVAL = 0.025; +	const F32 SHAKE_TOTAL_DURATION = 1.8; // the length of the default alert tone for this +	const F32 SHAKE_INITIAL_MAGNITUDE = 1.5; +	const F32 SHAKE_HORIZONTAL_BIAS = 0.25; +	F32 time_shaking; +	 +	if(SHAKE_START == sShakeState) +	{ +			mShakeTimer.setTimerExpirySec(SHAKE_INTERVAL); +			sShakeState = SHAKE_LEFT; +			mShakeIterations = 0; +			mShakeMagnitude = SHAKE_INITIAL_MAGNITUDE; +	} + +	if(SHAKE_DONE != sShakeState && mShakeTimer.hasExpired()) +	{ +		gAgentCamera.unlockView(); + +		switch(sShakeState) +		{ +			case SHAKE_LEFT: +				gAgentCamera.setPanLeftKey(mShakeMagnitude * SHAKE_HORIZONTAL_BIAS); +				sShakeState = SHAKE_UP; +				break; + +			case SHAKE_UP: +				gAgentCamera.setPanUpKey(mShakeMagnitude); +				sShakeState = SHAKE_RIGHT; +				break; + +			case SHAKE_RIGHT: +				gAgentCamera.setPanRightKey(mShakeMagnitude * SHAKE_HORIZONTAL_BIAS); +				sShakeState = SHAKE_DOWN; +				break; + +			case SHAKE_DOWN: +				gAgentCamera.setPanDownKey(mShakeMagnitude); +				mShakeIterations++; +				time_shaking = SHAKE_INTERVAL * (mShakeIterations * 4 /* left, up, right, down */); +				if(SHAKE_TOTAL_DURATION <= time_shaking) +				{ +					sShakeState = SHAKE_DONE; +					mShakeMagnitude = 0.0; +				} +				else +				{ +					sShakeState = SHAKE_LEFT; +					F32 percent_done_shaking = (SHAKE_TOTAL_DURATION - time_shaking) / SHAKE_TOTAL_DURATION; +					mShakeMagnitude = SHAKE_INITIAL_MAGNITUDE * (percent_done_shaking * percent_done_shaking); // exponential decay +				} +				break; + +			default: +				break; +		} +		mShakeTimer.setTimerExpirySec(SHAKE_INTERVAL); +	} +} + +void LLFloaterRegionRestarting::close() +{ +	LLFloaterRegionRestarting* floaterp = LLFloaterReg::findTypedInstance<LLFloaterRegionRestarting>("region_restarting"); + +	if (floaterp) +	{ +		floaterp->closeFloater(); +	} +} + +void LLFloaterRegionRestarting::updateTime(S32 time) +{ +	sSeconds = time; +	sShakeState = SHAKE_START; +} diff --git a/indra/newview/llfloaterregionrestarting.h b/indra/newview/llfloaterregionrestarting.h new file mode 100644 index 0000000000..46416db2c8 --- /dev/null +++ b/indra/newview/llfloaterregionrestarting.h @@ -0,0 +1,69 @@ +/**  + * @file llfloaterregionrestarting.h + * @brief Shows countdown timer during region restart + * + * $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_LLFLOATERREGIONRESTARTING_H +#define LL_LLFLOATERREGIONRESTARTING_H + +#include "llfloater.h" +#include "lltextbox.h" +#include "lleventtimer.h" + +class LLFloaterRegionRestarting : public LLFloater,  public LLEventTimer +{ +	friend class LLFloaterReg; + +public: +	static void close(); +	static void updateTime(S32 time); + +private: +	LLFloaterRegionRestarting(const LLSD& key); +	virtual ~LLFloaterRegionRestarting(); +	virtual BOOL postBuild(); +	virtual BOOL tick(); +	virtual void refresh(); +	virtual void draw(); +	virtual void regionChange(); + +	std::string mName; +	U32 mShakeIterations; +	F32 mShakeMagnitude; +	LLTimer mShakeTimer; + +	boost::signals2::connection mRegionChangedConnection; + +	enum +	{ +		SHAKE_START, +		SHAKE_LEFT, +		SHAKE_UP, +		SHAKE_RIGHT, +		SHAKE_DOWN, +		SHAKE_DONE +	}; +}; + +#endif // LL_LLFLOATERREGIONRESTARTING_H diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp index 76b73fcf29..68dbb5ae33 100755 --- a/indra/newview/llfloaterwebcontent.cpp +++ b/indra/newview/llfloaterwebcontent.cpp @@ -372,7 +372,10 @@ void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent  	}  	else if(event == MEDIA_EVENT_GEOMETRY_CHANGE)  	{ -		geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); +		if (mCurrentURL.find("facebook.com/dialog/oauth") == std::string::npos) // HACK to fix ACME-1317 - Cho +		{ +			geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); +		}  	}  	else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED )  	{ diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 137b5446cf..cb637c7162 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -627,8 +627,8 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)  	if (!sim_info)  	{  		// We haven't found a region for that point yet, leave the tracking to the world map -		LLWorldMap::getInstance()->setTracking(pos_global);  		LLTracker::stopTracking(NULL); +		LLWorldMap::getInstance()->setTracking(pos_global);  		S32 world_x = S32(pos_global.mdV[0] / 256);  		S32 world_y = S32(pos_global.mdV[1] / 256);  		LLWorldMapMessage::getInstance()->sendMapBlockRequest(world_x, world_y, world_x, world_y, true); @@ -643,9 +643,9 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)  	{  		// Down region. Show the blue circle of death!  		// i.e. let the world map that this and tell it it's invalid +		LLTracker::stopTracking(NULL);  		LLWorldMap::getInstance()->setTracking(pos_global);  		LLWorldMap::getInstance()->setTrackingInvalid(); -		LLTracker::stopTracking(NULL);  		setDefaultBtn("");  		// clicked on a down region - turn off coord display @@ -665,8 +665,8 @@ void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)  	std::string tooltip("");  	mTrackedStatus = LLTracker::TRACKING_LOCATION; -	LLTracker::trackLocation(pos_global, full_name, tooltip);  	LLWorldMap::getInstance()->cancelTracking();		// The floater is taking over the tracking +	LLTracker::trackLocation(pos_global, full_name, tooltip);  	LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();  	updateTeleportCoordsDisplay( coord_pos ); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 80ef506272..44943d8722 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -74,6 +74,7 @@  #include "llvoavatarself.h"  #include "llwearablelist.h"  #include "lllandmarkactions.h" +#include "llpanellandmarks.h"  void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); @@ -1449,6 +1450,38 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action)  			}  		}  	} +	else if ("show_on_map" == action) +	{ +		doActionOnCurSelectedLandmark(boost::bind(&LLItemBridge::doShowOnMap, this, _1)); +	} +} + +void LLItemBridge::doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb) +{ +	LLViewerInventoryItem* cur_item = getItem(); +	if(cur_item && cur_item->getInventoryType() == LLInventoryType::IT_LANDMARK) +	{  +		LLLandmark* landmark = LLLandmarkActions::getLandmark(cur_item->getUUID(), cb); +		if (landmark) +		{ +			cb(landmark); +		} +	} +} + +void LLItemBridge::doShowOnMap(LLLandmark* landmark) +{ +	LLVector3d landmark_global_pos; +	// landmark has already been tested for NULL by calling routine +	if (landmark->getGlobalPos(landmark_global_pos)) +	{ +		LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); +		if (!landmark_global_pos.isExactlyZero() && worldmap_instance) +		{ +			worldmap_instance->trackLocation(landmark_global_pos); +			LLFloaterReg::showInstance("world_map", "center"); +		} +	}  }  void copy_slurl_to_clipboard_callback_inv(const std::string& slurl) @@ -4580,6 +4613,7 @@ void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  		items.push_back(std::string("Landmark Separator"));  		items.push_back(std::string("url_copy"));  		items.push_back(std::string("About Landmark")); +		items.push_back(std::string("show_on_map"));  	}  	// Disable "About Landmark" menu item for diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 517153e171..bc875e8f37 100755 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -36,6 +36,7 @@  #include "llviewercontrol.h"  #include "llviewerwearable.h"  #include "lltooldraganddrop.h" +#include "lllandmarklist.h"  class LLInventoryFilter;  class LLInventoryPanel; @@ -239,7 +240,10 @@ protected:  	BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response);  	virtual BOOL isItemPermissive() const;  	virtual void buildDisplayName() const; +	void doActionOnCurSelectedLandmark(LLLandmarkList::loaded_callback_t cb); +private: +	void doShowOnMap(LLLandmark* landmark);  };  class LLFolderBridge : public LLInvFVBridge diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 5022dba934..dbdff11f11 100755 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -407,14 +407,14 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)  	// - Make the "Add landmark" button updated when either current parcel gets changed  	//   or a landmark gets created or removed from the inventory.  	// - Update the location string on parcel change. -	mParcelMgrConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback( +	mParcelMgrConnection = gAgent.addParcelChangedCallback(  		boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this));  	// LLLocationHistory instance is being created before the location input control, so we have to update initial state of button manually.  	mButton->setEnabled(LLLocationHistory::instance().getItemCount() > 0);  	mLocationHistoryConnection = LLLocationHistory::getInstance()->setChangedCallback(  			boost::bind(&LLLocationInputCtrl::onLocationHistoryChanged, this,_1)); -	mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this)); +	mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLLocationInputCtrl::onRegionBoundaryCrossed, this));  	createNavMeshStatusListenerForCurrentRegion();  	mRemoveLandmarkObserver	= new LLRemoveLandmarkObserver(this); diff --git a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp index a567d1217a..8879cfd7fb 100755 --- a/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp +++ b/indra/newview/llmenuoptionpathfindingrebakenavmesh.cpp @@ -79,7 +79,7 @@ void LLMenuOptionPathfindingRebakeNavmesh::initialize()  		if ( !mRegionCrossingSlot.connected() )  		{ -			mRegionCrossingSlot = LLEnvManagerNew::getInstance()->setRegionChangeCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this)); +			mRegionCrossingSlot = gAgent.addRegionChangedCallback(boost::bind(&LLMenuOptionPathfindingRebakeNavmesh::handleRegionBoundaryCrossed, this));  		}  		if (!mAgentStateSlot.connected()) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index ff73c42ec9..b0c8aa2d3a 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2005&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2014, 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 @@ -38,11 +38,13 @@  #include "llcallbacklist.h"  #include "llcurl.h"  #include "lldatapacker.h" +#include "lldeadmantimer.h"  #include "llfloatermodelpreview.h"  #include "llfloaterperms.h"  #include "lleconomy.h"  #include "llimagej2c.h"  #include "llhost.h" +#include "llmath.h"  #include "llnotificationsutil.h"  #include "llsd.h"  #include "llsdutil_math.h" @@ -52,6 +54,7 @@  #include "llviewercontrol.h"  #include "llviewerinventory.h"  #include "llviewermenufile.h" +#include "llviewermessage.h"  #include "llviewerobjectlist.h"  #include "llviewerregion.h"  #include "llviewertexturelist.h" @@ -65,6 +68,9 @@  #include "llfoldertype.h"  #include "llviewerparcelmgr.h"  #include "lluploadfloaterobservers.h" +#include "bufferarray.h" +#include "bufferstream.h" +#include "llfasttimer.h"  #include "boost/lexical_cast.hpp" @@ -72,11 +78,296 @@  #include "netdb.h"  #endif -#include <queue> + +// Purpose +// +//   The purpose of this module is to provide access between the viewer +//   and the asset system as regards to mesh objects. +// +//   * High-throughput download of mesh assets from servers while +//     following best industry practices for network profile. +//   * Reliable expensing and upload of new mesh assets. +//   * Recovery and retry from errors when appropriate. +//   * Decomposition of mesh assets for preview and uploads. +//   * And most important:  all of the above without exposing the +//     main thread to stalls due to deep processing or thread +//     locking actions.  In particular, the following operations +//     on LLMeshRepository are very averse to any stalls: +//     * loadMesh +//     * getMeshHeader (For structural details, see: +//       http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format) +//     * notifyLoadedMeshes +//     * getSkinInfo +// +// Threads +// +//   main     Main rendering thread, very sensitive to locking and other stalls +//   repo     Overseeing worker thread associated with the LLMeshRepoThread class +//   decom    Worker thread for mesh decomposition requests +//   core     HTTP worker thread:  does the work but doesn't intrude here +//   uploadN  0-N temporary mesh upload threads (0-1 in practice) +// +// Sequence of Operations +// +//   What follows is a description of the retrieval of one LOD for +//   a new mesh object.  Work is performed by a series of short, quick +//   actions distributed over a number of threads.  Each is meant +//   to proceed without stalling and the whole forms a deep request +//   pipeline to achieve throughput.  Ellipsis indicates a return +//   or break in processing which is resumed elsewhere. +// +//         main thread         repo thread (run() method) +// +//         loadMesh() invoked to request LOD +//           append LODRequest to mPendingRequests +//         ... +//         other mesh requests may be made +//         ... +//         notifyLoadedMeshes() invoked to stage work +//           append HeaderRequest to mHeaderReqQ +//         ... +//                             scan mHeaderReqQ +//                             issue 4096-byte GET for header +//                             ... +//                             onCompleted() invoked for GET +//                               data copied +//                               headerReceived() invoked +//                                 LLSD parsed +//                                 mMeshHeader, mMeshHeaderSize updated +//                                 scan mPendingLOD for LOD request +//                                 push LODRequest to mLODReqQ +//                             ... +//                             scan mLODReqQ +//                             fetchMeshLOD() invoked +//                               issue Byte-Range GET for LOD +//                             ... +//                             onCompleted() invoked for GET +//                               data copied +//                               lodReceived() invoked +//                                 unpack data into LLVolume +//                                 append LoadedMesh to mLoadedQ +//                             ... +//         notifyLoadedMeshes() invoked again +//           scan mLoadedQ +//           notifyMeshLoaded() for LOD +//             setMeshAssetLoaded() invoked for system volume +//             notifyMeshLoaded() invoked for each interested object +//         ... +// +// Mutexes +// +//   LLMeshRepository::mMeshMutex +//   LLMeshRepoThread::mMutex +//   LLMeshRepoThread::mHeaderMutex +//   LLMeshRepoThread::mSignal (LLCondition) +//   LLPhysicsDecomp::mSignal (LLCondition) +//   LLPhysicsDecomp::mMutex +//   LLMeshUploadThread::mMutex +// +// Mutex Order Rules +// +//   1.  LLMeshRepoThread::mMutex before LLMeshRepoThread::mHeaderMutex +//   2.  LLMeshRepository::mMeshMutex before LLMeshRepoThread::mMutex +//   (There are more rules, haven't been extracted.) +// +// Data Member Access/Locking +// +//   Description of how shared access to static and instance data +//   members is performed.  Each member is followed by the name of +//   the mutex, if any, covering the data and then a list of data +//   access models each of which is a triplet of the following form: +// +//     {ro, wo, rw}.{main, repo, any}.{mutex, none} +//     Type of access:  read-only, write-only, read-write. +//     Accessing thread or 'any' +//     Relevant mutex held during access (several may be held) or 'none' +// +//   A careful eye will notice some unsafe operations.  Many of these +//   have an alibi of some form.  Several types of alibi are identified +//   and listed here: +// +//     [0]  No alibi.  Probably unsafe. +//     [1]  Single-writer, self-consistent readers.  Old data must +//          be tolerated by any reader but data will come true eventually. +//     [2]  Like [1] but provides a hint about thread state.  These +//          may be unsafe. +//     [3]  empty() check outside of lock.  Can me made safish when +//          done in double-check lock style.  But this depends on +//          std:: implementation and memory model. +//     [4]  Appears to be covered by a mutex but doesn't need one. +//     [5]  Read of a double-checked lock. +// +//   So, in addition to documentation, take this as a to-do/review +//   list and see if you can improve things.  For porters to non-x86 +//   architectures, the weaker memory models will make these platforms +//   probabilistically more susceptible to hitting race conditions. +//   True here and in other multi-thread code such as texture fetching. +//   (Strong memory models make weak programmers.  Weak memory models +//   make strong programmers.  Ref:  arm, ppc, mips, alpha) +// +//   LLMeshRepository: +// +//     sBytesReceived                  none            rw.repo.none, ro.main.none [1] +//     sMeshRequestCount               " +//     sHTTPRequestCount               " +//     sHTTPLargeRequestCount          " +//     sHTTPRetryCount                 " +//     sHTTPErrorCount                 " +//     sLODPending                     mMeshMutex [4]  rw.main.mMeshMutex +//     sLODProcessing                  Repo::mMutex    rw.any.Repo::mMutex +//     sCacheBytesRead                 none            rw.repo.none, ro.main.none [1] +//     sCacheBytesWritten              " +//     sCacheReads                     " +//     sCacheWrites                    " +//     mLoadingMeshes                  mMeshMutex [4]  rw.main.none, rw.any.mMeshMutex +//     mSkinMap                        none            rw.main.none +//     mDecompositionMap               none            rw.main.none +//     mPendingRequests                mMeshMutex [4]  rw.main.mMeshMutex +//     mLoadingSkins                   mMeshMutex [4]  rw.main.mMeshMutex +//     mPendingSkinRequests            mMeshMutex [4]  rw.main.mMeshMutex +//     mLoadingDecompositions          mMeshMutex [4]  rw.main.mMeshMutex +//     mPendingDecompositionRequests   mMeshMutex [4]  rw.main.mMeshMutex +//     mLoadingPhysicsShapes           mMeshMutex [4]  rw.main.mMeshMutex +//     mPendingPhysicsShapeRequests    mMeshMutex [4]  rw.main.mMeshMutex +//     mUploads                        none            rw.main.none (upload thread accessing objects) +//     mUploadWaitList                 none            rw.main.none (upload thread accessing objects) +//     mInventoryQ                     mMeshMutex [4]  rw.main.mMeshMutex, ro.main.none [5] +//     mUploadErrorQ                   mMeshMutex      rw.main.mMeshMutex, rw.any.mMeshMutex +//     mGetMeshVersion                 none            rw.main.none +// +//   LLMeshRepoThread: +// +//     sActiveHeaderRequests    mMutex        rw.any.mMutex, ro.repo.none [1] +//     sActiveLODRequests       mMutex        rw.any.mMutex, ro.repo.none [1] +//     sMaxConcurrentRequests   mMutex        wo.main.none, ro.repo.none, ro.main.mMutex +//     mMeshHeader              mHeaderMutex  rw.repo.mHeaderMutex, ro.main.mHeaderMutex, ro.main.none [0] +//     mMeshHeaderSize          mHeaderMutex  rw.repo.mHeaderMutex +//     mSkinRequests            mMutex        rw.repo.mMutex, ro.repo.none [5] +//     mSkinInfoQ               mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0]) +//     mDecompositionRequests   mMutex        rw.repo.mMutex, ro.repo.none [5] +//     mPhysicsShapeRequests    mMutex        rw.repo.mMutex, ro.repo.none [5] +//     mDecompositionQ          mMutex        rw.repo.mMutex, rw.main.mMutex [5] (was:  [0]) +//     mHeaderReqQ              mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex +//     mLODReqQ                 mMutex        ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex +//     mUnavailableQ            mMutex        rw.repo.none [0], ro.main.none [5], rw.main.mMutex +//     mLoadedQ                 mMutex        rw.repo.mMutex, ro.main.none [5], rw.main.mMutex +//     mPendingLOD              mMutex        rw.repo.mMutex, rw.any.mMutex +//     mGetMeshCapability       mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0]) +//     mGetMesh2Capability      mMutex        rw.main.mMutex, ro.repo.mMutex (was:  [0]) +//     mGetMeshVersion          mMutex        rw.main.mMutex, ro.repo.mMutex +//     mHttp*                   none          rw.repo.none +// +//   LLMeshUploadThread: +// +//     mDiscarded               mMutex        rw.main.mMutex, ro.uploadN.none [1] +//     ... more ... +// +// QA/Development Testing +// +//   Debug variable 'MeshUploadFakeErrors' takes a mask of bits that will +//   simulate an error on fee query or upload.  Defined bits are: +// +//   0x01            Simulate application error on fee check reading +//                   response body from file "fake_upload_error.xml" +//   0x02            Same as 0x01 but for actual upload attempt. +//   0x04            Simulate a transport problem on fee check with a +//                   locally-generated 500 status. +//   0x08            As with 0x04 but for the upload operation. +// +//   For major changes, see the LL_MESH_FASTTIMER_ENABLE below and +//   instructions for looking for frame stalls using fast timers. +// +// *TODO:  Work list for followup actions: +//   * Review anything marked as unsafe above, verify if there are real issues. +//   * See if we can put ::run() into a hard sleep.  May not actually perform better +//     than the current scheme so be prepared for disappointment.  You'll likely +//     need to introduce a condition variable class that references a mutex in +//     methods rather than derives from mutex which isn't correct. +//   * On upload failures, make more information available to the alerting +//     dialog.  Get the structured information going into the log into a +//     tree there. +//   * Header parse failures come without much explanation.  Elaborate. +//   * Work queue for uploads?  Any need for this or is the current scheme good +//     enough? +//   * Various temp buffers used in VFS I/O might be allocated once or even +//     statically.  Look for some wins here. +//   * Move data structures holding mesh data used by main thread into main- +//     thread-only access so that no locking is needed.  May require duplication +//     of some data so that worker thread has a minimal data set to guide +//     operations. +// +// -------------------------------------------------------------------------- +//                    Development/Debug/QA Tools +// +// Enable here or in build environment to get fasttimer data on mesh fetches. +// +// Typically, this is used to perform A/B testing using the +// fasttimer console (shift-ctrl-9).  This is done by looking +// for stalls due to lock contention between the main thread +// and the repository and HTTP code.  In a release viewer, +// these appear as ping-time or worse spikes in frame time. +// With this instrumentation enabled, a stall will appear +// under the 'Mesh Fetch' timer which will be either top-level +// or under 'Render' time. + +#ifndef	LL_MESH_FASTTIMER_ENABLE +#define LL_MESH_FASTTIMER_ENABLE		1 +#endif +#if LL_MESH_FASTTIMER_ENABLE +static LLFastTimer::DeclareTimer FTM_MESH_FETCH("Mesh Fetch"); + +#define	MESH_FASTTIMER_DEFBLOCK			LLFastTimer meshtimer(FTM_MESH_FETCH) +#else +#define	MESH_FASTTIMER_DEFBLOCK +#endif // LL_MESH_FASTTIMER_ENABLE + + +// Random failure testing for development/QA. +// +// Set the MESH_*_FAILED macros to either 'false' or to +// an invocation of MESH_RANDOM_NTH_TRUE() with some +// suitable number.  In production, all must be false. +// +// Example: +// #define	MESH_HTTP_RESPONSE_FAILED				MESH_RANDOM_NTH_TRUE(9) + +// 1-in-N calls will test true +#define	MESH_RANDOM_NTH_TRUE(_N)				( ll_rand(S32(_N)) == 0 ) + +#define	MESH_HTTP_RESPONSE_FAILED				false +#define	MESH_HEADER_PROCESS_FAILED				false +#define	MESH_LOD_PROCESS_FAILED					false +#define	MESH_SKIN_INFO_PROCESS_FAILED			false +#define	MESH_DECOMP_PROCESS_FAILED				false +#define MESH_PHYS_SHAPE_PROCESS_FAILED			false + +// -------------------------------------------------------------------------- +  LLMeshRepository gMeshRepo; -const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; +const S32 MESH_HEADER_SIZE = 4096;                      // Important:  assumption is that headers fit in this space +const S32 REQUEST_HIGH_WATER_MIN = 32;					// Limits for GetMesh regions +const S32 REQUEST_HIGH_WATER_MAX = 150;					// Should remain under 2X throttle +const S32 REQUEST_LOW_WATER_MIN = 16; +const S32 REQUEST_LOW_WATER_MAX = 75; +const S32 REQUEST2_HIGH_WATER_MIN = 32;					// Limits for GetMesh2 regions +const S32 REQUEST2_HIGH_WATER_MAX = 80; +const S32 REQUEST2_LOW_WATER_MIN = 16; +const S32 REQUEST2_LOW_WATER_MAX = 40; +const U32 LARGE_MESH_FETCH_THRESHOLD = 1U << 21;		// Size at which requests goes to narrow/slow queue +const long SMALL_MESH_XFER_TIMEOUT = 120L;				// Seconds to complete xfer, small mesh downloads +const long LARGE_MESH_XFER_TIMEOUT = 600L;				// Seconds to complete xfer, large downloads + +// Would normally like to retry on uploads as some +// retryable failures would be recoverable.  Unfortunately, +// the mesh service is using 500 (retryable) rather than +// 400/bad request (permanent) for a bad payload and +// retrying that just leads to revocation of the one-shot +// cap which then produces a 404 on retry destroying some +// (occasionally) useful error information.  We'll leave +// upload retries to the user as in the past.  SH-4667. +const long UPLOAD_RETRY_LIMIT = 0L;  // Maximum mesh version to support.  Three least significant digits are reserved for the minor version,   // with major version changes indicating a format change that is not backwards compatible and should not @@ -87,35 +378,45 @@ const U32 MAX_MESH_REQUESTS_PER_SECOND = 100;  const S32 MAX_MESH_VERSION = 999;  U32 LLMeshRepository::sBytesReceived = 0; +U32 LLMeshRepository::sMeshRequestCount = 0;  U32 LLMeshRepository::sHTTPRequestCount = 0; +U32 LLMeshRepository::sHTTPLargeRequestCount = 0;  U32 LLMeshRepository::sHTTPRetryCount = 0; +U32 LLMeshRepository::sHTTPErrorCount = 0;  U32 LLMeshRepository::sLODProcessing = 0;  U32 LLMeshRepository::sLODPending = 0;  U32 LLMeshRepository::sCacheBytesRead = 0;  U32 LLMeshRepository::sCacheBytesWritten = 0; -U32 LLMeshRepository::sPeakKbps = 0; -	 +U32 LLMeshRepository::sCacheReads = 0; +U32 LLMeshRepository::sCacheWrites = 0; +U32 LLMeshRepository::sMaxLockHoldoffs = 0; -const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5; +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, false);	// true -> gather cpu metrics +	  static S32 dump_num = 0;  std::string make_dump_name(std::string prefix, S32 num)  {  	return prefix + boost::lexical_cast<std::string>(num) + std::string(".xml"); -	  }  void dump_llsd_to_file(const LLSD& content, std::string filename);  LLSD llsd_from_file(std::string filename); -std::string header_lod[] =  +const std::string header_lod[] =   {  	"lowest_lod",  	"low_lod",  	"medium_lod",  	"high_lod"  }; +const char * const LOG_MESH = "Mesh"; +// Static data and functions to measure mesh load +// time metrics for a new region scene. +static unsigned int metrics_teleport_start_count = 0; +boost::signals2::connection metrics_teleport_started_signal; +static void teleport_started();  //get the number of bytes resident in memory for given volume  U32 get_volume_memory_size(const LLVolume* volume) @@ -197,200 +498,228 @@ void get_vertex_buffer_from_mesh(LLCDMeshData& mesh, LLModel::PhysicsMesh& res,  	}  } -S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -S32 LLMeshRepoThread::sActiveLODRequests = 0; +volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; +volatile S32 LLMeshRepoThread::sActiveLODRequests = 0;  U32	LLMeshRepoThread::sMaxConcurrentRequests = 1; - -class LLMeshHeaderResponder : public LLCurl::Responder +S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; +S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; +S32 LLMeshRepoThread::sRequestWaterLevel = 0; + +// Base handler class for all mesh users of llcorehttp. +// This is roughly equivalent to a Responder class in +// traditional LL code.  The base is going to perform +// common response/data handling in the inherited +// onCompleted() method.  Derived classes, one for each +// type of HTTP action, define processData() and +// processFailure() methods to customize handling and +// error messages. +// +// LLCore::HttpHandler +//   LLMeshHandlerBase +//     LLMeshHeaderHandler +//     LLMeshLODHandler +//     LLMeshSkinInfoHandler +//     LLMeshDecompositionHandler +//     LLMeshPhysicsShapeHandler +//   LLMeshUploadThread + +class LLMeshHandlerBase : public LLCore::HttpHandler  {  public: -	LLVolumeParams mMeshParams; -	bool mProcessed; +	LLMeshHandlerBase() +		: LLCore::HttpHandler(), +		  mMeshParams(), +		  mProcessed(false), +		  mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) +		{} + +	virtual ~LLMeshHandlerBase() +		{} + +protected: +	LLMeshHandlerBase(const LLMeshHandlerBase &);				// Not defined +	void operator=(const LLMeshHandlerBase &);					// Not defined +	 +public: +	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size) = 0; +	virtual void processFailure(LLCore::HttpStatus status) = 0; +	 +public: +	LLVolumeParams		mMeshParams; +	bool				mProcessed; +	LLCore::HttpHandle	mHttpHandle; +}; -	LLMeshHeaderResponder(const LLVolumeParams& mesh_params) -		: mMeshParams(mesh_params) -	{ -		LLMeshRepoThread::incActiveHeaderRequests(); -		mProcessed = false; -	} -	~LLMeshHeaderResponder() +// Subclass for header fetches. +// +// Thread:  repo +class LLMeshHeaderHandler : public LLMeshHandlerBase +{ +public: +	LLMeshHeaderHandler(const LLVolumeParams & mesh_params) +		: LLMeshHandlerBase()  	{ -		if (!LLApp::isQuitting()) -		{ -			if (!mProcessed) -			{ //something went wrong, retry -				llwarns << "Timeout or service unavailable, retrying." << llendl; -				LLMeshRepository::sHTTPRetryCount++; -				LLMeshRepoThread::HeaderRequest req(mMeshParams); -				LLMutexLock lock(gMeshRepo.mThread->mMutex); -				gMeshRepo.mThread->mHeaderReqQ.push(req); -			} - -			LLMeshRepoThread::decActiveHeaderRequests(); -		} +		mMeshParams = mesh_params; +		LLMeshRepoThread::incActiveHeaderRequests();  	} +	virtual ~LLMeshHeaderHandler(); -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer); - +protected: +	LLMeshHeaderHandler(const LLMeshHeaderHandler &);			// Not defined +	void operator=(const LLMeshHeaderHandler &);				// Not defined +	 +public: +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processFailure(LLCore::HttpStatus status);  }; -class LLMeshLODResponder : public LLCurl::Responder + +// Subclass for LOD fetches. +// +// Thread:  repo +class LLMeshLODHandler : public LLMeshHandlerBase  {  public: -	LLVolumeParams mMeshParams; -	S32 mLOD; -	U32 mRequestedBytes; -	U32 mOffset; -	bool mProcessed; - -	LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes) -		: mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes) +	LLMeshLODHandler(const LLVolumeParams & mesh_params, S32 lod, U32 offset, U32 requested_bytes) +		: LLMeshHandlerBase(), +		  mLOD(lod), +		  mRequestedBytes(requested_bytes), +		  mOffset(offset)  	{ +		mMeshParams = mesh_params;  		LLMeshRepoThread::incActiveLODRequests(); -		mProcessed = false;  	} +	virtual ~LLMeshLODHandler(); +	 +protected: +	LLMeshLODHandler(const LLMeshLODHandler &);					// Not defined +	void operator=(const LLMeshLODHandler &);					// Not defined +	 +public: +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processFailure(LLCore::HttpStatus status); -	~LLMeshLODResponder() -	{ -		if (!LLApp::isQuitting()) -		{ -			if (!mProcessed) -			{ -				llwarns << "Killed without being processed, retrying." << llendl; -				LLMeshRepository::sHTTPRetryCount++; -				gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD); -			} -			LLMeshRepoThread::decActiveLODRequests(); -		} -	} - -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer); - -}; - -class LLMeshSkinInfoResponder : public LLCurl::Responder -{  public: -	LLUUID mMeshID; +	S32 mLOD;  	U32 mRequestedBytes;  	U32 mOffset; -	bool mProcessed; - -	LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size) -		: mMeshID(id), mRequestedBytes(size), mOffset(offset) -	{ -		mProcessed = false; -	} - -	~LLMeshSkinInfoResponder() -	{ -		if (!LLApp::isQuitting() && -			!mProcessed && -			mMeshID.notNull()) -		{	// Something went wrong, retry -			llwarns << "Timeout or service unavailable, retrying loadMeshSkinInfo() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); -		} -	} - -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer); -  }; -class LLMeshDecompositionResponder : public LLCurl::Responder + +// Subclass for skin info fetches. +// +// Thread:  repo +class LLMeshSkinInfoHandler : public LLMeshHandlerBase  {  public: +	LLMeshSkinInfoHandler(const LLUUID& id, U32 offset, U32 size) +		: LLMeshHandlerBase(), +		  mMeshID(id), +		  mRequestedBytes(size), +		  mOffset(offset) +	{} +	virtual ~LLMeshSkinInfoHandler(); + +protected: +	LLMeshSkinInfoHandler(const LLMeshSkinInfoHandler &);		// Not defined +	void operator=(const LLMeshSkinInfoHandler &);				// Not defined +	 +public: +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processFailure(LLCore::HttpStatus status); + +public:  	LLUUID mMeshID;  	U32 mRequestedBytes;  	U32 mOffset; -	bool mProcessed; - -	LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size) -		: mMeshID(id), mRequestedBytes(size), mOffset(offset) -	{ -		mProcessed = false; -	} - -	~LLMeshDecompositionResponder() -	{ -		if (!LLApp::isQuitting() && -			!mProcessed && -			mMeshID.notNull()) -		{	// Something went wrong, retry -			llwarns << "Timeout or service unavailable, retrying loadMeshDecomposition() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshDecomposition(mMeshID); -		} -	} - -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer); -  }; -class LLMeshPhysicsShapeResponder : public LLCurl::Responder + +// Subclass for decomposition fetches. +// +// Thread:  repo +class LLMeshDecompositionHandler : public LLMeshHandlerBase  {  public: +	LLMeshDecompositionHandler(const LLUUID& id, U32 offset, U32 size) +		: LLMeshHandlerBase(), +		  mMeshID(id), +		  mRequestedBytes(size), +		  mOffset(offset) +	{} +	virtual ~LLMeshDecompositionHandler(); + +protected: +	LLMeshDecompositionHandler(const LLMeshDecompositionHandler &);		// Not defined +	void operator=(const LLMeshDecompositionHandler &);					// Not defined +	 +public: +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processFailure(LLCore::HttpStatus status); + +public:  	LLUUID mMeshID;  	U32 mRequestedBytes;  	U32 mOffset; -	bool mProcessed; +}; -	LLMeshPhysicsShapeResponder(const LLUUID& id, U32 offset, U32 size) -		: mMeshID(id), mRequestedBytes(size), mOffset(offset) -	{ -		mProcessed = false; -	} -	~LLMeshPhysicsShapeResponder() -	{ -		if (!LLApp::isQuitting() && -			!mProcessed && -			mMeshID.notNull()) -		{	// Something went wrong, retry -			llwarns << "Timeout or service unavailable, retrying loadMeshPhysicsShape() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); -		} -	} - -	virtual void completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer); +// Subclass for physics shape fetches. +// +// Thread:  repo +class LLMeshPhysicsShapeHandler : public LLMeshHandlerBase +{ +public: +	LLMeshPhysicsShapeHandler(const LLUUID& id, U32 offset, U32 size) +		: LLMeshHandlerBase(), +		  mMeshID(id), +		  mRequestedBytes(size), +		  mOffset(offset) +	{} +	virtual ~LLMeshPhysicsShapeHandler(); + +protected: +	LLMeshPhysicsShapeHandler(const LLMeshPhysicsShapeHandler &);	// Not defined +	void operator=(const LLMeshPhysicsShapeHandler &);				// Not defined +	 +public: +	virtual void processData(LLCore::BufferArray * body, U8 * data, S32 data_size); +	virtual void processFailure(LLCore::HttpStatus status); +public: +	LLUUID		mMeshID; +	U32			mRequestedBytes; +	U32			mOffset;  }; -void log_upload_error(S32 status, const LLSD& content, std::string stage, std::string model_name) + +void log_upload_error(LLCore::HttpStatus status, const LLSD& content, +					  const char * const stage, const std::string & model_name)  {  	// Add notification popup.  	LLSD args; -	std::string message = content["error"]["message"]; -	std::string identifier = content["error"]["identifier"]; +	std::string message = content["error"]["message"].asString(); +	std::string identifier = content["error"]["identifier"].asString();  	args["MESSAGE"] = message;  	args["IDENTIFIER"] = identifier;  	args["LABEL"] = model_name;  	gMeshRepo.uploadError(args);  	// Log details. -	llwarns << "stage: " << stage << " http status: " << status << llendl; +	LL_WARNS(LOG_MESH) << "Error in stage:  " << stage +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ")" << LL_ENDL;  	if (content.has("error"))  	{  		const LLSD& err = content["error"]; -		llwarns << "err: " << err << llendl; -		llwarns << "mesh upload failed, stage '" << stage -				<< "' error '" << err["error"].asString() -				<< "', message '" << err["message"].asString() -				<< "', id '" << err["identifier"].asString() -				<< "'" << llendl; +		LL_WARNS(LOG_MESH) << "error: " << err << LL_ENDL; +		LL_WARNS(LOG_MESH) << "  mesh upload failed, stage '" << stage +						   << "', error '" << err["error"].asString() +						   << "', message '" << err["message"].asString() +						   << "', id '" << err["identifier"].asString() +						   << "'" << LL_ENDL;  		if (err.has("errors"))  		{  			S32 error_num = 0; @@ -400,13 +729,13 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s  				 ++it)  			{  				const LLSD& err_entry = *it; -				llwarns << "error[" << error_num << "]:" << llendl; +				LL_WARNS(LOG_MESH) << "  error[" << error_num << "]:" << LL_ENDL;  				for (LLSD::map_const_iterator map_it = err_entry.beginMap();  					 map_it != err_entry.endMap();  					 ++map_it)  				{ -					llwarns << "\t" << map_it->first << ": " -							<< map_it->second << llendl; +					LL_WARNS(LOG_MESH) << "    " << map_it->first << ":  " +									   << map_it->second << LL_ENDL;  				}  				error_num++;  			} @@ -414,153 +743,72 @@ void log_upload_error(S32 status, const LLSD& content, std::string stage, std::s  	}  	else  	{ -		llwarns << "bad mesh, no error information available" << llendl; +		LL_WARNS(LOG_MESH) << "Bad response to mesh request, no additional error information available." << LL_ENDL;  	}  } -class LLWholeModelFeeResponder: public LLCurl::Responder -{ -	LLMeshUploadThread* mThread; -	LLSD mModelData; -	LLHandle<LLWholeModelFeeObserver> mObserverHandle; -public: -	LLWholeModelFeeResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle<LLWholeModelFeeObserver> observer_handle): -		mThread(thread), -		mModelData(model_data), -		mObserverHandle(observer_handle) -	{ -		if (mThread) -		{ -			mThread->startRequest(); -		} -	} - -	~LLWholeModelFeeResponder() -	{ -		if (mThread) -		{ -			mThread->stopRequest(); -		} -	} - -	virtual void completed(U32 status, -						   const std::string& reason, -						   const LLSD& content) -	{ -		LLSD cc = content; -		if (gSavedSettings.getS32("MeshUploadFakeErrors")&1) -		{ -			cc = llsd_from_file("fake_upload_error.xml"); -		} -			 -		dump_llsd_to_file(cc,make_dump_name("whole_model_fee_response_",dump_num)); -		LLWholeModelFeeObserver* observer = mObserverHandle.get(); +LLMeshRepoThread::LLMeshRepoThread() +: LLThread("mesh repo"), +  mHttpRequest(NULL), +  mHttpOptions(NULL), +  mHttpLargeOptions(NULL), +  mHttpHeaders(NULL), +  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), +  mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), +  mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), +  mHttpPriority(0), +  mGetMeshVersion(2) +{  +	mMutex = new LLMutex(NULL); +	mHeaderMutex = new LLMutex(NULL); +	mSignal = new LLCondition(NULL); +	mHttpRequest = new LLCore::HttpRequest; +	mHttpOptions = new LLCore::HttpOptions; +	mHttpOptions->setTransferTimeout(SMALL_MESH_XFER_TIMEOUT); +	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); +	mHttpLargeOptions = new LLCore::HttpOptions; +	mHttpLargeOptions->setTransferTimeout(LARGE_MESH_XFER_TIMEOUT); +	mHttpLargeOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); +	mHttpHeaders = new LLCore::HttpHeaders; +	mHttpHeaders->append("Accept", "application/vnd.ll.mesh"); +	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH2); +	mHttpLegacyPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_MESH1); +	mHttpLargePolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_LARGE_MESH); +} -		if (isGoodStatus(status) && -			cc["state"].asString() == "upload") -		{ -			mThread->mWholeModelUploadURL = cc["uploader"].asString(); -			if (observer) -			{ -				cc["data"]["upload_price"] = cc["upload_price"]; -				observer->onModelPhysicsFeeReceived(cc["data"], mThread->mWholeModelUploadURL); -			} -		} -		else -		{ -			llwarns << "fee request failed" << llendl; -			log_upload_error(status,cc,"fee",mModelData["name"]); -			mThread->mWholeModelUploadURL = ""; +LLMeshRepoThread::~LLMeshRepoThread() +{ +	LL_INFOS(LOG_MESH) << "Small GETs issued:  " << LLMeshRepository::sHTTPRequestCount +					   << ", Large GETs issued:  " << LLMeshRepository::sHTTPLargeRequestCount +					   << ", Max Lock Holdoffs:  " << LLMeshRepository::sMaxLockHoldoffs +					   << LL_ENDL; -			if (observer) -			{ -				observer->setModelPhysicsFeeErrorStatus(status, reason); -			} -		} +	for (http_request_set::iterator iter(mHttpRequestSet.begin()); +		 iter != mHttpRequestSet.end(); +		 ++iter) +	{ +		delete *iter;  	} - -}; - -class LLWholeModelUploadResponder: public LLCurl::Responder -{ -	LLMeshUploadThread* mThread; -	LLSD mModelData; -	LLHandle<LLWholeModelUploadObserver> mObserverHandle; -	 -public: -	LLWholeModelUploadResponder(LLMeshUploadThread* thread, LLSD& model_data, LLHandle<LLWholeModelUploadObserver> observer_handle): -		mThread(thread), -		mModelData(model_data), -		mObserverHandle(observer_handle) +	mHttpRequestSet.clear(); +	if (mHttpHeaders)  	{ -		if (mThread) -		{ -			mThread->startRequest(); -		} +		mHttpHeaders->release(); +		mHttpHeaders = NULL;  	} - -	~LLWholeModelUploadResponder() +	if (mHttpOptions)  	{ -		if (mThread) -		{ -			mThread->stopRequest(); -		} +		mHttpOptions->release(); +		mHttpOptions = NULL;  	} - -	virtual void completed(U32 status, -						   const std::string& reason, -						   const LLSD& content) +	if (mHttpLargeOptions)  	{ -		LLSD cc = content; -		if (gSavedSettings.getS32("MeshUploadFakeErrors")&2) -		{ -			cc = llsd_from_file("fake_upload_error.xml"); -		} - -		dump_llsd_to_file(cc,make_dump_name("whole_model_upload_response_",dump_num)); -		 -		LLWholeModelUploadObserver* observer = mObserverHandle.get(); - -		// requested "mesh" asset type isn't actually the type -		// of the resultant object, fix it up here. -		if (isGoodStatus(status) && -			cc["state"].asString() == "complete") -		{ -			mModelData["asset_type"] = "object"; -			gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData,cc)); - -			if (observer) -			{ -				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); -			} -		} -		else -		{ -			llwarns << "upload failed" << llendl; -			std::string model_name = mModelData["name"].asString(); -			log_upload_error(status,cc,"upload",model_name); - -			if (observer) -			{ -				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); -			} -		} +		mHttpLargeOptions->release(); +		mHttpLargeOptions = NULL;  	} -}; - -LLMeshRepoThread::LLMeshRepoThread() -: LLThread("mesh repo")  -{  -	mWaiting = false; -	mMutex = new LLMutex(NULL); -	mHeaderMutex = new LLMutex(NULL); -	mSignal = new LLCondition(NULL); -} - -LLMeshRepoThread::~LLMeshRepoThread() -{ +	delete mHttpRequest; +	mHttpRequest = NULL;  	delete mMutex;  	mMutex = NULL;  	delete mHeaderMutex; @@ -571,109 +819,180 @@ LLMeshRepoThread::~LLMeshRepoThread()  void LLMeshRepoThread::run()  { -	mCurlRequest = new LLCurlRequest();  	LLCDResult res = LLConvexDecomposition::initThread();  	if (res != LLCD_OK)  	{ -		llwarns << "convex decomposition unable to be loaded" << llendl; +		LL_WARNS(LOG_MESH) << "Convex decomposition unable to be loaded.  Expect severe problems." << LL_ENDL;  	}  	while (!LLApp::isQuitting())  	{ -		mWaiting = true; +		// *TODO:  Revise sleep/wake strategy and try to move away +		// from polling operations in this thread.  We can sleep +		// this thread hard when: +		// * All Http requests are serviced +		// * LOD request queue empty +		// * Header request queue empty +		// * Skin info request queue empty +		// * Decomposition request queue empty +		// * Physics shape request queue empty +		// We wake the thread when any of the above become untrue. +		// Will likely need a correctly-implemented condition variable to do this. +		// On the other hand, this may actually be an effective and efficient scheme... +		  		mSignal->wait(); -		mWaiting = false; -		if (!LLApp::isQuitting()) +		if (LLApp::isQuitting())  		{ -			static U32 count = 0; +			break; +		} +		 +		if (! mHttpRequestSet.empty()) +		{ +			// Dispatch all HttpHandler notifications +			mHttpRequest->update(0L); +		} +		sRequestWaterLevel = mHttpRequestSet.size();			// Stats data update +			 +		// NOTE: order of queue processing intentionally favors LOD requests over header requests -			static F32 last_hundred = gFrameTimeSeconds; +		while (!mLODReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) +		{ +			if (! mMutex) +			{ +				break; +			} +			mMutex->lock(); +			LODRequest req = mLODReqQ.front(); +			mLODReqQ.pop(); +			LLMeshRepository::sLODProcessing--; +			mMutex->unlock(); +			if (!fetchMeshLOD(req.mMeshParams, req.mLOD))		// failed, resubmit +			{ +				mMutex->lock(); +				mLODReqQ.push(req) ;  +				++LLMeshRepository::sLODProcessing; +				mMutex->unlock(); +			} +		} -			if (gFrameTimeSeconds - last_hundred > 1.f) -			{ //a second has gone by, clear count -				last_hundred = gFrameTimeSeconds; -				count = 0;	 +		while (!mHeaderReqQ.empty() && mHttpRequestSet.size() < sRequestHighWater) +		{ +			if (! mMutex) +			{ +				break; +			} +			mMutex->lock(); +			HeaderRequest req = mHeaderReqQ.front(); +			mHeaderReqQ.pop(); +			mMutex->unlock(); +			if (!fetchMeshHeader(req.mMeshParams))//failed, resubmit +			{ +				mMutex->lock(); +				mHeaderReqQ.push(req) ; +				mMutex->unlock();  			} +		} -			// NOTE: throttling intentionally favors LOD requests over header requests -			 -			while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests) +		// For the final three request lists, similar goal to above but +		// slightly different queue structures.  Stay off the mutex when +		// performing long-duration actions. + +		if (mHttpRequestSet.size() < sRequestHighWater +			&& (! mSkinRequests.empty() +				|| ! mDecompositionRequests.empty() +				|| ! mPhysicsShapeRequests.empty())) +		{ +			// Something to do probably, lock and double-check.  We don't want +			// to hold the lock long here.  That will stall main thread activities +			// so we bounce it. + +			mMutex->lock(); +			if (! mSkinRequests.empty() && mHttpRequestSet.size() < sRequestHighWater)  			{ -				if (mMutex) +				std::set<LLUUID> incomplete; +				std::set<LLUUID>::iterator iter(mSkinRequests.begin()); +				while (iter != mSkinRequests.end() && mHttpRequestSet.size() < sRequestHighWater)  				{ -					mMutex->lock(); -					LODRequest req = mLODReqQ.front(); -					mLODReqQ.pop(); -					LLMeshRepository::sLODProcessing--; +					LLUUID mesh_id = *iter; +					mSkinRequests.erase(iter);  					mMutex->unlock(); -					if (!fetchMeshLOD(req.mMeshParams, req.mLOD, count))//failed, resubmit + +					if (! fetchMeshSkinInfo(mesh_id))  					{ -						mMutex->lock(); -						mLODReqQ.push(req);  -						mMutex->unlock(); +						incomplete.insert(mesh_id);  					} + +					mMutex->lock(); +					iter = mSkinRequests.begin();  				} -			} -			while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests) -			{ -				if (mMutex) +				if (! incomplete.empty())  				{ -					mMutex->lock(); -					HeaderRequest req = mHeaderReqQ.front(); -					mHeaderReqQ.pop(); -					mMutex->unlock(); -					if (!fetchMeshHeader(req.mMeshParams, count))//failed, resubmit -					{ -						mMutex->lock(); -						mHeaderReqQ.push(req) ; -						mMutex->unlock(); -					} +					mSkinRequests.insert(incomplete.begin(), incomplete.end());  				}  			} -			{ //mSkinRequests is protected by mSignal +			// holding lock, try next list +			// *TODO:  For UI/debug-oriented lists, we might drop the fine- +			// grained locking as there's a lowered expectation of smoothness +			// in these cases. +			if (! mDecompositionRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) +			{  				std::set<LLUUID> incomplete; -				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) +				std::set<LLUUID>::iterator iter(mDecompositionRequests.begin()); +				while (iter != mDecompositionRequests.end() && mHttpRequestSet.size() < sRequestHighWater)  				{  					LLUUID mesh_id = *iter; -					if (!fetchMeshSkinInfo(mesh_id)) +					mDecompositionRequests.erase(iter); +					mMutex->unlock(); +					 +					if (! fetchMeshDecomposition(mesh_id))  					{  						incomplete.insert(mesh_id);  					} + +					mMutex->lock(); +					iter = mDecompositionRequests.begin();  				} -				mSkinRequests = incomplete; -			} -			{ //mDecompositionRequests is protected by mSignal -				std::set<LLUUID> incomplete; -				for (std::set<LLUUID>::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) +				if (! incomplete.empty())  				{ -					LLUUID mesh_id = *iter; -					if (!fetchMeshDecomposition(mesh_id)) -					{ -						incomplete.insert(mesh_id); -					} +					mDecompositionRequests.insert(incomplete.begin(), incomplete.end());  				} -				mDecompositionRequests = incomplete;  			} -			{ //mPhysicsShapeRequests is protected by mSignal +			// holding lock, final list +			if (! mPhysicsShapeRequests.empty() && mHttpRequestSet.size() < sRequestHighWater) +			{  				std::set<LLUUID> incomplete; -				for (std::set<LLUUID>::iterator iter = mPhysicsShapeRequests.begin(); iter != mPhysicsShapeRequests.end(); ++iter) +				std::set<LLUUID>::iterator iter(mPhysicsShapeRequests.begin()); +				while (iter != mPhysicsShapeRequests.end() && mHttpRequestSet.size() < sRequestHighWater)  				{  					LLUUID mesh_id = *iter; -					if (!fetchMeshPhysicsShape(mesh_id)) +					mPhysicsShapeRequests.erase(iter); +					mMutex->unlock(); +					 +					if (! fetchMeshPhysicsShape(mesh_id))  					{  						incomplete.insert(mesh_id);  					} + +					mMutex->lock(); +					iter = mPhysicsShapeRequests.begin();  				} -				mPhysicsShapeRequests = incomplete; -			} -			mCurlRequest->process(); +				if (! incomplete.empty()) +				{ +					mPhysicsShapeRequests.insert(incomplete.begin(), incomplete.end()); +				} +			} +			mMutex->unlock();  		} + +		// For dev purposes only.  A dynamic change could make this false +		// and that shouldn't assert. +		// llassert_always(mHttpRequestSet.size() <= sRequestHighWater);  	}  	if (mSignal->isLocked()) @@ -684,25 +1003,25 @@ void LLMeshRepoThread::run()  	res = LLConvexDecomposition::quitThread();  	if (res != LLCD_OK)  	{ -		llwarns << "convex decomposition unable to be quit" << llendl; +		LL_WARNS(LOG_MESH) << "Convex decomposition unable to be quit." << LL_ENDL;  	} - -	delete mCurlRequest; -	mCurlRequest = NULL;  } +// Mutex:  LLMeshRepoThread::mMutex must be held on entry  void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{  	mSkinRequests.insert(mesh_id);  } +// Mutex:  LLMeshRepoThread::mMutex must be held on entry  void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{  	mDecompositionRequests.insert(mesh_id);  } +// Mutex:  LLMeshRepoThread::mMutex must be held on entry  void LLMeshRepoThread::loadMeshPhysicsShape(const LLUUID& mesh_id) -{ //protected by mSignal, no locking needed here +{  	mPhysicsShapeRequests.insert(mesh_id);  } @@ -715,7 +1034,6 @@ void LLMeshRepoThread::lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32  } -  void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  { //could be called from any thread  	LLMutexLock lock(mMutex); @@ -747,31 +1065,122 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  	}  } -//static  -std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) +// Mutex:  must be holding mMutex when called +void LLMeshRepoThread::setGetMeshCaps(const std::string & get_mesh1, +									  const std::string & get_mesh2, +									  int pref_version)  { -	std::string http_url; +	mGetMeshCapability = get_mesh1; +	mGetMesh2Capability = get_mesh2; +	mGetMeshVersion = pref_version; +} + + +// Constructs a Cap URL for the mesh.  Prefers a GetMesh2 cap +// over a GetMesh cap. +// +// Mutex:  acquires mMutex +void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * version) +{ +	std::string res_url; +	int res_version(2);  	if (gAgent.getRegion())  	{ -		http_url = gMeshRepo.mGetMeshCapability;  +		LLMutexLock lock(mMutex); + +		// Get a consistent pair of (cap string, version).  The +		// locking could be eliminated here without loss of safety +		// by using a set of staging values in setGetMeshCaps(). +		 +		if (! mGetMesh2Capability.empty() && mGetMeshVersion > 1) +		{ +			res_url = mGetMesh2Capability; +			res_version = 2; +		} +		else +		{ +			res_url = mGetMeshCapability; +			res_version = 1; +		}  	} -	if (!http_url.empty()) +	if (! res_url.empty())  	{ -		http_url += "/?mesh_id="; -		http_url += mesh_id.asString().c_str(); +		res_url += "/?mesh_id="; +		res_url += mesh_id.asString().c_str();  	}  	else  	{ -		llwarns << "Current region does not have GetMesh capability!  Cannot load " << mesh_id << ".mesh" << llendl; +		LL_WARNS_ONCE(LOG_MESH) << "Current region does not have GetMesh capability!  Cannot load " +								<< mesh_id << ".mesh" << LL_ENDL;  	} -	return http_url; +	*url = res_url; +	*version = res_version; +} + +// Issue an HTTP GET request with byte range using the right +// policy class.  Large requests go to the large request class. +// If the current region supports GetMesh2, we prefer that for +// smaller requests otherwise we try to use the traditional +// GetMesh capability and connection concurrency. +// +// @return		Valid handle or LLCORE_HTTP_HANDLE_INVALID. +//				If the latter, actual status is found in +//				mHttpStatus member which is valid until the +//				next call to this method. +// +// Thread:  repo +LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version, +												  size_t offset, size_t len, +												  LLCore::HttpHandler * handler) +{ +	LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); +	 +	if (len < LARGE_MESH_FETCH_THRESHOLD) +	{ +		handle = mHttpRequest->requestGetByteRange((2 == cap_version +													? mHttpPolicyClass +													: mHttpLegacyPolicyClass), +												   mHttpPriority, +												   url, +												   offset, +												   len, +												   mHttpOptions, +												   mHttpHeaders, +												   handler); +		if (LLCORE_HTTP_HANDLE_INVALID != handle) +		{ +			++LLMeshRepository::sHTTPRequestCount; +		} +	} +	else +	{ +		handle = mHttpRequest->requestGetByteRange(mHttpLargePolicyClass, +												   mHttpPriority, +												   url, +												   offset, +												   len, +												   mHttpLargeOptions, +												   mHttpHeaders, +												   handler); +		if (LLCORE_HTTP_HANDLE_INVALID != handle) +		{ +			++LLMeshRepository::sHTTPLargeRequestCount; +		} +	} +	if (LLCORE_HTTP_HANDLE_INVALID == handle) +	{ +		// Something went wrong, capture the error code for caller. +		mHttpStatus = mHttpRequest->getStatus(); +	} +	return handle;  } +  bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) -{ //protected by mMutex +{  	if (!mHeaderMutex)  	{ @@ -786,7 +1195,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  		return false;  	} -	bool ret = true ; +	++LLMeshRepository::sMeshRequestCount; +	bool ret = true;  	U32 header_size = mMeshHeaderSize[mesh_id];  	if (header_size > 0) @@ -804,6 +1214,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  			if (file.getSize() >= offset+size)  			{				  				LLMeshRepository::sCacheBytesRead += size; +				++LLMeshRepository::sCacheReads;  				file.seek(offset);  				U8* buffer = new U8[size];  				file.read(buffer, size); @@ -828,17 +1239,28 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  			}  			//reading from VFS failed for whatever reason, fetch from sim -			std::vector<std::string> headers; -			headers.push_back("Accept: application/octet-stream"); +			int cap_version(2); +			std::string http_url; +			constructUrl(mesh_id, &http_url, &cap_version); -			std::string http_url = constructUrl(mesh_id);  			if (!http_url.empty()) -			{				 -				ret = mCurlRequest->getByteRange(http_url, headers, offset, size, -												 new LLMeshSkinInfoResponder(mesh_id, offset, size)); -				if(ret) +			{ +				LLMeshSkinInfoHandler * handler = new LLMeshSkinInfoHandler(mesh_id, offset, size); +				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); +				if (LLCORE_HTTP_HANDLE_INVALID == handle) +				{ +					LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID +									   << ".  Reason:  " << mHttpStatus.toString() +									   << " (" << mHttpStatus.toTerseString() << ")" +									   << LL_ENDL; +					delete handler; +					ret = false; + +				} +				else  				{ -					LLMeshRepository::sHTTPRequestCount++; +					handler->mHttpHandle = handle; +					mHttpRequestSet.insert(handler);  				}  			}  		} @@ -853,7 +1275,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id)  }  bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) -{ //protected by mMutex +{  	if (!mHeaderMutex)  	{  		return false; @@ -867,8 +1289,9 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)  		return false;  	} +	++LLMeshRepository::sMeshRequestCount;  	U32 header_size = mMeshHeaderSize[mesh_id]; -	bool ret = true ; +	bool ret = true;  	if (header_size > 0)  	{ @@ -885,6 +1308,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)  			if (file.getSize() >= offset+size)  			{  				LLMeshRepository::sCacheBytesRead += size; +				++LLMeshRepository::sCacheReads;  				file.seek(offset);  				U8* buffer = new U8[size]; @@ -910,17 +1334,27 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)  			}  			//reading from VFS failed for whatever reason, fetch from sim -			std::vector<std::string> headers; -			headers.push_back("Accept: application/octet-stream"); - -			std::string http_url = constructUrl(mesh_id); +			int cap_version(2); +			std::string http_url; +			constructUrl(mesh_id, &http_url, &cap_version); +			  			if (!http_url.empty()) -			{				 -				ret = mCurlRequest->getByteRange(http_url, headers, offset, size, -												 new LLMeshDecompositionResponder(mesh_id, offset, size)); -				if(ret) +			{ +				LLMeshDecompositionHandler * handler = new LLMeshDecompositionHandler(mesh_id, offset, size); +				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); +				if (LLCORE_HTTP_HANDLE_INVALID == handle) +				{ +					LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID +									   << ".  Reason:  " << mHttpStatus.toString() +									   << " (" << mHttpStatus.toTerseString() << ")" +									   << LL_ENDL; +					delete handler; +					ret = false; +				} +				else  				{ -					LLMeshRepository::sHTTPRequestCount++; +					handler->mHttpHandle = handle; +					mHttpRequestSet.insert(handler);  				}  			}  		} @@ -935,7 +1369,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id)  }  bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) -{ //protected by mMutex +{  	if (!mHeaderMutex)  	{  		return false; @@ -949,8 +1383,9 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)  		return false;  	} +	++LLMeshRepository::sMeshRequestCount;  	U32 header_size = mMeshHeaderSize[mesh_id]; -	bool ret = true ; +	bool ret = true;  	if (header_size > 0)  	{ @@ -967,6 +1402,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)  			if (file.getSize() >= offset+size)  			{  				LLMeshRepository::sCacheBytesRead += size; +				++LLMeshRepository::sCacheReads;  				file.seek(offset);  				U8* buffer = new U8[size];  				file.read(buffer, size); @@ -991,18 +1427,27 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id)  			}  			//reading from VFS failed for whatever reason, fetch from sim -			std::vector<std::string> headers; -			headers.push_back("Accept: application/octet-stream"); - -			std::string http_url = constructUrl(mesh_id); +			int cap_version(2); +			std::string http_url; +			constructUrl(mesh_id, &http_url, &cap_version); +			  			if (!http_url.empty()) -			{				 -				ret = mCurlRequest->getByteRange(http_url, headers, offset, size, -												 new LLMeshPhysicsShapeResponder(mesh_id, offset, size)); - -				if(ret) +			{ +				LLMeshPhysicsShapeHandler * handler = new LLMeshPhysicsShapeHandler(mesh_id, offset, size); +				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); +				if (LLCORE_HTTP_HANDLE_INVALID == handle)  				{ -					LLMeshRepository::sHTTPRequestCount++; +					LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID +									   << ".  Reason:  " << mHttpStatus.toString() +									   << " (" << mHttpStatus.toTerseString() << ")" +									   << LL_ENDL; +					delete handler; +					ret = false; +				} +				else +				{ +					handler->mHttpHandle = handle; +					mHttpRequestSet.insert(handler);  				}  			}  		} @@ -1049,8 +1494,10 @@ void LLMeshRepoThread::decActiveHeaderRequests()  }  //return false if failed to get header -bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count) +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params)  { +	++LLMeshRepository::sMeshRequestCount; +  	{  		//look for mesh in asset in vfs  		LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); @@ -1058,43 +1505,57 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, U32& c  		S32 size = file.getSize();  		if (size > 0) -		{ //NOTE -- if the header size is ever more than 4KB, this will break -			U8 buffer[4096]; -			S32 bytes = llmin(size, 4096); +		{ +			// *NOTE:  if the header size is ever more than 4KB, this will break +			U8 buffer[MESH_HEADER_SIZE]; +			S32 bytes = llmin(size, MESH_HEADER_SIZE);  			LLMeshRepository::sCacheBytesRead += bytes;	 +			++LLMeshRepository::sCacheReads;  			file.read(buffer, bytes);  			if (headerReceived(mesh_params, buffer, bytes)) -			{ //did not do an HTTP request, return false +			{ +				// Found mesh in VFS cache  				return true;  			}  		}  	}  	//either cache entry doesn't exist or is corrupt, request header from simulator	 -	bool retval = true ; -	std::vector<std::string> headers; -	headers.push_back("Accept: application/octet-stream"); - -	std::string http_url = constructUrl(mesh_params.getSculptID()); +	bool retval = true; +	int cap_version(2); +	std::string http_url; +	constructUrl(mesh_params.getSculptID(), &http_url, &cap_version); +	  	if (!http_url.empty())  	{  		//grab first 4KB if we're going to bother with a fetch.  Cache will prevent future fetches if a full mesh fits  		//within the first 4KB  		//NOTE -- this will break of headers ever exceed 4KB		 -		retval = mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); -		if(retval) + +		LLMeshHeaderHandler * handler = new LLMeshHeaderHandler(mesh_params); +		LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); +		if (LLCORE_HTTP_HANDLE_INVALID == handle) +		{ +			LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID +							   << ".  Reason:  " << mHttpStatus.toString() +							   << " (" << mHttpStatus.toTerseString() << ")" +							   << LL_ENDL; +			delete handler; +			retval = false; +		} +		else  		{ -			LLMeshRepository::sHTTPRequestCount++; +			handler->mHttpHandle = handle; +			mHttpRequestSet.insert(handler);  		} -		count++;  	}  	return retval;  }  //return false if failed to get mesh lod. -bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count) -{ //protected by mMutex +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{  	if (!mHeaderMutex)  	{  		return false; @@ -1102,6 +1563,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,  	mHeaderMutex->lock(); +	++LLMeshRepository::sMeshRequestCount;  	bool retval = true;  	LLUUID mesh_id = mesh_params.getSculptID(); @@ -1123,6 +1585,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,  			if (file.getSize() >= offset+size)  			{  				LLMeshRepository::sCacheBytesRead += size; +				++LLMeshRepository::sCacheReads;  				file.seek(offset);  				U8* buffer = new U8[size];  				file.read(buffer, size); @@ -1147,20 +1610,29 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,  			}  			//reading from VFS failed for whatever reason, fetch from sim -			std::vector<std::string> headers; -			headers.push_back("Accept: application/octet-stream"); - -			std::string http_url = constructUrl(mesh_id); +			int cap_version(2); +			std::string http_url; +			constructUrl(mesh_id, &http_url, &cap_version); +			  			if (!http_url.empty()) -			{				 -				retval = mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, -										   new LLMeshLODResponder(mesh_params, lod, offset, size)); - -				if(retval) +			{ +				LLMeshLODHandler * handler = new LLMeshLODHandler(mesh_params, lod, offset, size); +				LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); +				if (LLCORE_HTTP_HANDLE_INVALID == handle) +				{ +					LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID +									   << ".  Reason:  " << mHttpStatus.toString() +									   << " (" << mHttpStatus.toTerseString() << ")" +									   << LL_ENDL; +					delete handler; +					retval = false; +				} +				else  				{ -					LLMeshRepository::sHTTPRequestCount++; +					handler->mHttpHandle = handle; +					mHttpRequestSet.insert(handler); +					// *NOTE:  Allowing a re-request, not marking as unavailable.  Is that correct?  				} -				count++;  			}  			else  			{ @@ -1182,6 +1654,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod,  bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size)  { +	const LLUUID mesh_id = mesh_params.getSculptID();  	LLSD header;  	U32 header_size = 0; @@ -1202,7 +1675,8 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat  		if (!LLSDSerialize::fromBinary(header, stream, data_size))  		{ -			llwarns << "Mesh header parse error.  Not a valid mesh asset!" << llendl; +			LL_WARNS(LOG_MESH) << "Mesh header parse error.  Not a valid mesh asset!  ID:  " << mesh_id +							   << LL_ENDL;  			return false;  		} @@ -1210,13 +1684,12 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat  	}  	else  	{ -		llinfos -			<< "Marking header as non-existent, will not retry." << llendl; +		LL_INFOS(LOG_MESH) << "Non-positive data size.  Marking header as non-existent, will not retry.  ID:  " << mesh_id +						   << LL_ENDL;  		header["404"] = 1;  	}  	{ -		LLUUID mesh_id = mesh_params.getSculptID();  		{  			LLMutexLock lock(mHeaderMutex); @@ -1224,6 +1697,7 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat  			mMeshHeader[mesh_id] = header;  		} +		  		LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time.  		//check for pending requests @@ -1277,7 +1751,8 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat  		if (!unzip_llsd(skin, stream, data_size))  		{ -			llwarns << "Mesh skin info parse error.  Not a valid mesh asset!" << llendl; +			LL_WARNS(LOG_MESH) << "Mesh skin info parse error.  Not a valid mesh asset!  ID:  " << mesh_id +							   << LL_ENDL;  			return false;  		}  	} @@ -1286,8 +1761,11 @@ bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 dat  		LLMeshSkinInfo info(skin);  		info.mMeshID = mesh_id; -		//llinfos<<"info pelvis offset"<<info.mPelvisOffset<<llendl; -		mSkinInfoQ.push(info); +		// LL_DEBUGS(LOG_MESH) << "info pelvis offset" << info.mPelvisOffset << LL_ENDL; +		{ +			LLMutexLock lock(mMutex); +			mSkinInfoQ.push_back(info); +		}  	}  	return true; @@ -1305,7 +1783,8 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3  		if (!unzip_llsd(decomp, stream, data_size))  		{ -			llwarns << "Mesh decomposition parse error.  Not a valid mesh asset!" << llendl; +			LL_WARNS(LOG_MESH) << "Mesh decomposition parse error.  Not a valid mesh asset!  ID:  " << mesh_id +							   << LL_ENDL;  			return false;  		}  	} @@ -1313,7 +1792,10 @@ bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S3  	{  		LLModel::Decomposition* d = new LLModel::Decomposition(decomp);  		d->mMeshID = mesh_id; -		mDecompositionQ.push(d); +		{ +			LLMutexLock lock(mMutex); +			mDecompositionQ.push_back(d); +		}  	}  	return true; @@ -1372,15 +1854,20 @@ bool LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_id, U8* data, S32  		}  	} -	mDecompositionQ.push(d); +	{ +		LLMutexLock lock(mMutex); +		mDecompositionQ.push_back(d); +	}  	return true;  }  LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures, -										bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, -					   LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer) -: LLThread("mesh upload"), -	mDiscarded(FALSE), +									   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload, +									   LLHandle<LLWholeModelFeeObserver> fee_observer, +									   LLHandle<LLWholeModelUploadObserver> upload_observer) +  : LLThread("mesh upload"), +	LLCore::HttpHandler(), +	mDiscarded(false),  	mDoUpload(do_upload),  	mWholeModelUploadURL(upload_url),  	mFeeObserverHandle(fee_observer), @@ -1391,7 +1878,6 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,  	mUploadSkin = upload_skin;  	mUploadJoints = upload_joints;  	mMutex = new LLMutex(NULL); -	mCurlRequest = NULL;  	mPendingUploads = 0;  	mFinished = false;  	mOrigin = gAgent.getPositionAgent(); @@ -1401,12 +1887,33 @@ LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data,  	mOrigin += gAgent.getAtAxis() * scale.magVec(); -	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut") ; +	mMeshUploadTimeOut = gSavedSettings.getS32("MeshUploadTimeOut"); + +	mHttpRequest = new LLCore::HttpRequest; +	mHttpOptions = new LLCore::HttpOptions; +	mHttpOptions->setTransferTimeout(mMeshUploadTimeOut); +	mHttpOptions->setUseRetryAfter(gSavedSettings.getBOOL("MeshUseHttpRetryAfter")); +	mHttpOptions->setRetries(UPLOAD_RETRY_LIMIT); +	mHttpHeaders = new LLCore::HttpHeaders; +	mHttpHeaders->append("Content-Type", "application/llsd+xml"); +	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_UPLOADS); +	mHttpPriority = 0;  }  LLMeshUploadThread::~LLMeshUploadThread()  { - +	if (mHttpHeaders) +	{ +		mHttpHeaders->release(); +		mHttpHeaders = NULL; +	} +	if (mHttpOptions) +	{ +		mHttpOptions->release(); +		mHttpOptions = NULL; +	} +	delete mHttpRequest; +	mHttpRequest = NULL;  }  LLMeshUploadThread::DecompRequest::DecompRequest(LLModel* mdl, LLModel* base_model, LLMeshUploadThread* thread) @@ -1448,14 +1955,14 @@ void LLMeshUploadThread::preStart()  void LLMeshUploadThread::discard()  { -	LLMutexLock lock(mMutex) ; -	mDiscarded = TRUE ; +	LLMutexLock lock(mMutex); +	mDiscarded = true;  } -BOOL LLMeshUploadThread::isDiscarded() +bool LLMeshUploadThread::isDiscarded() const  { -	LLMutexLock lock(mMutex) ; -	return mDiscarded ; +	LLMutexLock lock(mMutex); +	return mDiscarded;  }  void LLMeshUploadThread::run() @@ -1706,9 +2213,15 @@ void LLMeshUploadThread::generateHulls()  		}  	} -	if(has_valid_requests) -	{ -		while (!mPhysicsComplete) +	if (has_valid_requests) +	{ +		// *NOTE:  Interesting livelock condition on shutdown.  If there +		// is an upload request in generateHulls() when shutdown starts, +		// the main thread isn't available to manage communication between +		// the decomposition thread and the upload thread and this loop +		// wouldn't complete in turn stalling the main thread.  The check +		// on isDiscarded() prevents that. +		while (! mPhysicsComplete && ! isDiscarded())  		{  			apr_sleep(100);  		} @@ -1717,86 +2230,266 @@ void LLMeshUploadThread::generateHulls()  void LLMeshUploadThread::doWholeModelUpload()  { -	mCurlRequest = new LLCurlRequest(); +	LL_DEBUGS(LOG_MESH) << "Starting model upload.  Instances:  " << mInstance.size() << LL_ENDL;  	if (mWholeModelUploadURL.empty())  	{ -		llinfos << "unable to upload, fee request failed" << llendl; +		LL_WARNS(LOG_MESH) << "Missing mesh upload capability, unable to upload, fee request failed." +						   << LL_ENDL;  	}  	else  	{  		generateHulls(); - -		LLSD full_model_data; -		wholeModelToLLSD(full_model_data, true); -		LLSD body = full_model_data["asset_resources"]; -		dump_llsd_to_file(body,make_dump_name("whole_model_body_",dump_num)); -		LLCurlRequest::headers_t headers; - +		LL_DEBUGS(LOG_MESH) << "Hull generation completed." << LL_ENDL; + +		mModelData = LLSD::emptyMap(); +		wholeModelToLLSD(mModelData, true); +		LLSD body = mModelData["asset_resources"]; +		dump_llsd_to_file(body, make_dump_name("whole_model_body_", dump_num)); + +		LLCore::BufferArray * ba = new LLCore::BufferArray; +		LLCore::BufferArrayStream bas(ba); +		LLSDSerialize::toXML(body, bas); +		// LLSDSerialize::toXML(mModelData, bas);		// <- Enabling this will generate a convenient upload error +		LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, +															  mHttpPriority, +															  mWholeModelUploadURL, +															  ba, +															  mHttpOptions, +															  mHttpHeaders, +															  this); +		ba->release(); +		 +		if (LLCORE_HTTP_HANDLE_INVALID == handle) +		{ +			mHttpStatus = mHttpRequest->getStatus(); +		 +			LL_WARNS(LOG_MESH) << "Couldn't issue request for full model upload.  Reason:  " << mHttpStatus.toString() +							   << " (" << mHttpStatus.toTerseString() << ")" +							   << LL_ENDL; +		} +		else  		{ -			LLCurl::ResponderPtr responder = new LLWholeModelUploadResponder(this, full_model_data, mUploadObserverHandle) ; +			U32 sleep_time(10); +		 +			LL_DEBUGS(LOG_MESH) << "POST request issued." << LL_ENDL; +			 +			mHttpRequest->update(0); +			while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) +			{ +				ms_sleep(sleep_time); +				sleep_time = llmin(250U, sleep_time + sleep_time); +				mHttpRequest->update(0); +			} -			while(!mCurlRequest->post(mWholeModelUploadURL, headers, body, responder, mMeshUploadTimeOut)) +			if (isDiscarded()) +			{ +				LL_DEBUGS(LOG_MESH) << "Mesh upload operation discarded." << LL_ENDL; +			} +			else  			{ -				//sleep for 10ms to prevent eating a whole core -				apr_sleep(10000); +				LL_DEBUGS(LOG_MESH) << "Mesh upload operation completed." << LL_ENDL;  			}  		} - -		do -		{ -			mCurlRequest->process(); -			//sleep for 10ms to prevent eating a whole core -			apr_sleep(10000); -		} while (!LLAppViewer::isQuitting() && mPendingUploads > 0);  	} - -	delete mCurlRequest; -	mCurlRequest = NULL; - -	// Currently a no-op. -	mFinished = true;  }  void LLMeshUploadThread::requestWholeModelFee()  {  	dump_num++; -	mCurlRequest = new LLCurlRequest(); -  	generateHulls(); -	LLSD model_data; -	wholeModelToLLSD(model_data,false); -	dump_llsd_to_file(model_data,make_dump_name("whole_model_fee_request_",dump_num)); - -	LLCurlRequest::headers_t headers; +	mModelData = LLSD::emptyMap(); +	wholeModelToLLSD(mModelData, false); +	dump_llsd_to_file(mModelData, make_dump_name("whole_model_fee_request_", dump_num)); +	LLCore::BufferArray * ba = new LLCore::BufferArray; +	LLCore::BufferArrayStream bas(ba); +	LLSDSerialize::toXML(mModelData, bas); +		 +	LLCore::HttpHandle handle = mHttpRequest->requestPost(mHttpPolicyClass, +														  mHttpPriority, +														  mWholeModelFeeCapability, +														  ba, +														  mHttpOptions, +														  mHttpHeaders, +														  this); +	ba->release(); +	if (LLCORE_HTTP_HANDLE_INVALID == handle) +	{ +		mHttpStatus = mHttpRequest->getStatus(); +		 +		LL_WARNS(LOG_MESH) << "Couldn't issue request for model fee.  Reason:  " << mHttpStatus.toString() +						   << " (" << mHttpStatus.toTerseString() << ")" +						   << LL_ENDL; +	} +	else  	{ -		LLCurl::ResponderPtr responder = new LLWholeModelFeeResponder(this,model_data, mFeeObserverHandle) ; -		while(!mCurlRequest->post(mWholeModelFeeCapability, headers, model_data, responder, mMeshUploadTimeOut)) +		U32 sleep_time(10); +		 +		mHttpRequest->update(0); +		while (! LLApp::isQuitting() && ! finished() && ! isDiscarded()) +		{ +			ms_sleep(sleep_time); +			sleep_time = llmin(250U, sleep_time + sleep_time); +			mHttpRequest->update(0); +		} +		if (isDiscarded())  		{ -			//sleep for 10ms to prevent eating a whole core -			apr_sleep(10000); +			LL_DEBUGS(LOG_MESH) << "Mesh fee query operation discarded." << LL_ENDL;  		}  	} +} -	do -	{ -		mCurlRequest->process(); -		//sleep for 10ms to prevent eating a whole core -		apr_sleep(10000); -	} while (!LLApp::isQuitting() && mPendingUploads > 0); -	delete mCurlRequest; -	mCurlRequest = NULL; +// Does completion duty for both fee queries and actual uploads. +void LLMeshUploadThread::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ +	// QA/Devel:  0x2 to enable fake error import on upload, 0x1 on fee check +	const S32 fake_error(gSavedSettings.getS32("MeshUploadFakeErrors") & (mDoUpload ? 0xa : 0x5)); +	LLCore::HttpStatus status(response->getStatus()); +	if (fake_error) +	{ +		status = (fake_error & 0x0c) ? LLCore::HttpStatus(500) : LLCore::HttpStatus(200); +	} +	std::string reason(status.toString()); +	LLSD body; -	// Currently a no-op.  	mFinished = true; + +	if (mDoUpload) +	{ +		// model upload case +		LLWholeModelUploadObserver * observer(mUploadObserverHandle.get()); + +		if (! status) +		{ +			LL_WARNS(LOG_MESH) << "Upload failed.  Reason:  " << reason +							   << " (" << status.toTerseString() << ")" +							   << LL_ENDL; + +			// Build a fake body for the alert generator +			body["error"] = LLSD::emptyMap(); +			body["error"]["message"] = reason; +			body["error"]["identifier"] = "NetworkError";		// from asset-upload/upload_util.py +			log_upload_error(status, body, "upload", mModelData["name"].asString()); + +			if (observer) +			{ +				doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); +			} +		} +		else +		{ +			if (fake_error & 0x2) +			{ +				body = llsd_from_file("fake_upload_error.xml"); +			} +			else +			{ +				LLCore::BufferArray * ba(response->getBody()); +				if (ba && ba->size()) +				{ +					LLCore::BufferArrayStream bas(ba); +					LLSDSerialize::fromXML(body, bas); +				} +			} +			dump_llsd_to_file(body, make_dump_name("whole_model_upload_response_", dump_num)); + +			if (body["state"].asString() == "complete") +			{ +				// requested "mesh" asset type isn't actually the type +				// of the resultant object, fix it up here. +				mModelData["asset_type"] = "object"; +				gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mModelData, body)); + +				if (observer) +				{ +					doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadSuccess, observer)); +				} +			} +			else +			{ +				LL_WARNS(LOG_MESH) << "Upload failed.  Not in expected 'complete' state." << LL_ENDL; +				log_upload_error(status, body, "upload", mModelData["name"].asString()); + +				if (observer) +				{ +					doOnIdleOneTime(boost::bind(&LLWholeModelUploadObserver::onModelUploadFailure, observer)); +				} +			} +		} +	} +	else +	{ +		// model fee case +		LLWholeModelFeeObserver* observer(mFeeObserverHandle.get()); +		mWholeModelUploadURL.clear(); +		 +		if (! status) +		{ +			LL_WARNS(LOG_MESH) << "Fee request failed.  Reason:  " << reason +							   << " (" << status.toTerseString() << ")" +							   << LL_ENDL; + +			// Build a fake body for the alert generator +			body["error"] = LLSD::emptyMap(); +			body["error"]["message"] = reason; +			body["error"]["identifier"] = "NetworkError";		// from asset-upload/upload_util.py +			log_upload_error(status, body, "fee", mModelData["name"].asString()); + +			if (observer) +			{ +				observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason); +			} +		} +		else +		{ +			if (fake_error & 0x1) +			{ +				body = llsd_from_file("fake_upload_error.xml"); +			} +			else +			{ +				LLCore::BufferArray * ba(response->getBody()); +				if (ba && ba->size()) +				{ +					LLCore::BufferArrayStream bas(ba); +					LLSDSerialize::fromXML(body, bas); +				} +			} +			dump_llsd_to_file(body, make_dump_name("whole_model_fee_response_", dump_num)); +		 +			if (body["state"].asString() == "upload") +			{ +				mWholeModelUploadURL = body["uploader"].asString(); + +				if (observer) +				{ +					body["data"]["upload_price"] = body["upload_price"]; +					observer->onModelPhysicsFeeReceived(body["data"], mWholeModelUploadURL); +				} +			} +			else +			{ +				LL_WARNS(LOG_MESH) << "Fee request failed.  Not in expected 'upload' state." << LL_ENDL; +				log_upload_error(status, body, "fee", mModelData["name"].asString()); + +				if (observer) +				{ +					observer->setModelPhysicsFeeErrorStatus(status.toULong(), reason); +				} +			} +		} +	}  } +  void LLMeshRepoThread::notifyLoadedMeshes()  { +	bool update_metrics(false); +	  	if (!mMutex)  	{  		return; @@ -1805,10 +2498,16 @@ void LLMeshRepoThread::notifyLoadedMeshes()  	while (!mLoadedQ.empty())  	{  		mMutex->lock(); +		if (mLoadedQ.empty()) +		{ +			mMutex->unlock(); +			break; +		}  		LoadedMesh mesh = mLoadedQ.front();  		mLoadedQ.pop();  		mMutex->unlock(); +		update_metrics = true;  		if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0)  		{  			gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); @@ -1823,24 +2522,59 @@ void LLMeshRepoThread::notifyLoadedMeshes()  	while (!mUnavailableQ.empty())  	{  		mMutex->lock(); +		if (mUnavailableQ.empty()) +		{ +			mMutex->unlock(); +			break; +		} +		  		LODRequest req = mUnavailableQ.front();  		mUnavailableQ.pop();  		mMutex->unlock(); -		 + +		update_metrics = true;  		gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD);  	} -	while (!mSkinInfoQ.empty()) +	if (! mSkinInfoQ.empty() || ! mDecompositionQ.empty())  	{ -		gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front()); -		mSkinInfoQ.pop(); +		if (mMutex->trylock()) +		{ +			std::list<LLMeshSkinInfo> skin_info_q; +			std::list<LLModel::Decomposition*> decomp_q; + +			if (! mSkinInfoQ.empty()) +			{ +				skin_info_q.swap(mSkinInfoQ); +			} +			if (! mDecompositionQ.empty()) +			{ +				decomp_q.swap(mDecompositionQ); +			} + +			mMutex->unlock(); + +			// Process the elements free of the lock +			while (! skin_info_q.empty()) +			{ +				gMeshRepo.notifySkinInfoReceived(skin_info_q.front()); +				skin_info_q.pop_front(); +			} + +			while (! decomp_q.empty()) +			{ +				gMeshRepo.notifyDecompositionReceived(decomp_q.front()); +				decomp_q.pop_front(); +			} +		}  	} -	while (!mDecompositionQ.empty()) +	if (update_metrics)  	{ -		gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front()); -		mDecompositionQ.pop(); +		// Ping time-to-load metrics for mesh download operations. +		LLMeshRepository::metricsProgress(0);  	} +	  }  S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod)  @@ -1919,245 +2653,250 @@ void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header)  } -void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) +void LLMeshHandlerBase::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response)  {  	mProcessed = true; -	 -	// thread could have already be destroyed during logout -	if( !gMeshRepo.mThread ) -	{ -		return; -	} -	 -	S32 data_size = buffer->countAfter(channels.in(), NULL); -	if (status < 200 || status > 400) +	unsigned int retries(0U); +	response->getRetries(NULL, &retries); +	LLMeshRepository::sHTTPRetryCount += retries; + +	LLCore::HttpStatus status(response->getStatus()); +	if (! status || MESH_HTTP_RESPONSE_FAILED)  	{ -		llwarns << status << ": " << reason << llendl; +		processFailure(status); +		++LLMeshRepository::sHTTPErrorCount;  	} - -	if (data_size < mRequestedBytes) +	else  	{ -		if (status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE) -		{ //timeout or service unavailable, try again -			llwarns << "Timeout or service unavailable, retrying." << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); -		} -		else +		// From texture fetch code and may apply here: +		// +		// A warning about partial (HTTP 206) data.  Some grid services +		// do *not* return a 'Content-Range' header in the response to +		// Range requests with a 206 status.  We're forced to assume +		// we get what we asked for in these cases until we can fix +		// the services. +		// +		// May also need to deal with 200 status (full asset returned +		// rather than partial) and 416 (request completely unsatisfyable). +		// Always been exposed to these but are less likely here where +		// speculative loads aren't done. +		static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + +		if (par_status != status)  		{ -			llassert(status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE); //intentionally trigger a breakpoint -			llwarns << "Unhandled status " << status << llendl; +			LL_WARNS_ONCE(LOG_MESH) << "Non-206 successful status received for fetch:  " +									<< status.toTerseString() << LL_ENDL;  		} -		return; -	} +		 +		LLCore::BufferArray * body(response->getBody()); +		S32 data_size(body ? body->size() : 0); +		U8 * data(NULL); -	LLMeshRepository::sBytesReceived += mRequestedBytes; +		if (data_size > 0) +		{ +			// *TODO: Try to get rid of data copying and add interfaces +			// that support BufferArray directly.  Introduce a two-phase +			// handler, optional first that takes a body, fallback second +			// that requires a temporary allocation and data copy. +			data = new U8[data_size]; +			body->read(0, (char *) data, data_size); +			LLMeshRepository::sBytesReceived += data_size; +		} -	U8* data = NULL; +		processData(body, data, data_size); -	if (data_size > 0) -	{ -		data = new U8[data_size]; -		buffer->readAfter(channels.in(), NULL, data, data_size); +		delete [] data;  	} -	if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) -	{ -		//good fetch from sim, write to VFS for caching -		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); +	// Release handler +	gMeshRepo.mThread->mHttpRequestSet.erase(this); +	delete this;		// Must be last statement +} -		S32 offset = mOffset; -		S32 size = mRequestedBytes; -		if (file.getSize() >= offset+size) +LLMeshHeaderHandler::~LLMeshHeaderHandler() +{ +	if (!LLApp::isQuitting()) +	{ +		if (! mProcessed)  		{ -			file.seek(offset); -			file.write(data, size); -			LLMeshRepository::sCacheBytesWritten += size; +			// something went wrong, retry +			LL_WARNS(LOG_MESH) << "Mesh header fetch canceled unexpectedly, retrying." << LL_ENDL; +			LLMeshRepoThread::HeaderRequest req(mMeshParams); +			LLMutexLock lock(gMeshRepo.mThread->mMutex); +			gMeshRepo.mThread->mHeaderReqQ.push(req);  		} +		LLMeshRepoThread::decActiveHeaderRequests();  	} - -	delete [] data;  } -void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) +void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status)  { -	mProcessed = true; +	LL_WARNS(LOG_MESH) << "Error during mesh header handling.  ID:  " << mMeshParams.getSculptID() +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ").  Not retrying." +					   << LL_ENDL; -	// thread could have already be destroyed during logout -	if( !gMeshRepo.mThread ) +	// Can't get the header so none of the LODs will be available +	LLMutexLock lock(gMeshRepo.mThread->mMutex); +	for (int i(0); i < 4; ++i)  	{ -		return; +		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));  	} +} -	S32 data_size = buffer->countAfter(channels.in(), NULL); - -	if (status < 200 || status > 400) +void LLMeshHeaderHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ +	LLUUID mesh_id = mMeshParams.getSculptID(); +	bool success = (! MESH_HEADER_PROCESS_FAILED) && gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); +	llassert(success); +	if (! success)  	{ -		llwarns << status << ": " << reason << llendl; -	} +		// *TODO:  Get real reason for parse failure here.  Might we want to retry? +		LL_WARNS(LOG_MESH) << "Unable to parse mesh header.  ID:  " << mesh_id +						   << ", Unknown reason.  Not retrying." +						   << LL_ENDL; -	if (data_size < mRequestedBytes) -	{ -		if (status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE) -		{ //timeout or service unavailable, try again -			llwarns << "Timeout or service unavailable, retrying loadMeshSkinInfo() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); -		} -		else +		// Can't get the header so none of the LODs will be available +		LLMutexLock lock(gMeshRepo.mThread->mMutex); +		for (int i(0); i < 4; ++i)  		{ -			llassert(status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE); //intentionally trigger a breakpoint -			llwarns << "Unhandled status " << status << llendl; +			gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, i));  		} -		return;  	} +	else if (data && data_size > 0) +	{ +		// header was successfully retrieved from sim, cache in vfs +		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; -	LLMeshRepository::sBytesReceived += mRequestedBytes; +		S32 version = header["version"].asInteger(); -	U8* data = NULL; +		if (version <= MAX_MESH_VERSION) +		{ +			std::stringstream str; -	if (data_size > 0) -	{ -		data = new U8[data_size]; -		buffer->readAfter(channels.in(), NULL, data, data_size); -	} +			S32 lod_bytes = 0; -	if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) -	{ -		//good fetch from sim, write to VFS for caching -		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); +			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) +			{ +				// figure out how many bytes we'll need to reserve in the file +				const std::string & lod_name = header_lod[i]; +				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); +			} +		 +			// just in case skin info or decomposition is at the end of the file (which it shouldn't be) +			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); +			lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); -		S32 offset = mOffset; -		S32 size = mRequestedBytes; +			S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; +			S32 bytes = lod_bytes + header_bytes;  -		if (file.getSize() >= offset+size) -		{ -			LLMeshRepository::sCacheBytesWritten += size; -			file.seek(offset); -			file.write(data, size); -		} -	} +		 +			// It's possible for the remote asset to have more data than is needed for the local cache +			// only allocate as much space in the VFS as is needed for the local cache +			data_size = llmin(data_size, bytes); -	delete [] data; -} +			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); +			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) +			{ +				LLMeshRepository::sCacheBytesWritten += data_size; +				++LLMeshRepository::sCacheWrites; -void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) -{ -	mProcessed = true; -	 -	if( !gMeshRepo.mThread ) -	{ -		return; -	} +				file.write(data, data_size); +			 +				// zero out the rest of the file  +				U8 block[MESH_HEADER_SIZE]; +				memset(block, 0, sizeof(block)); -	S32 data_size = buffer->countAfter(channels.in(), NULL); +				while (bytes-file.tell() > sizeof(block)) +				{ +					file.write(block, sizeof(block)); +				} -	if (status < 200 || status > 400) -	{ -		llwarns << status << ": " << reason << llendl; +				S32 remaining = bytes-file.tell(); +				if (remaining > 0) +				{ +					file.write(block, remaining); +				} +			} +		}  	} +} -	if (data_size < mRequestedBytes) +LLMeshLODHandler::~LLMeshLODHandler() +{ +	if (! LLApp::isQuitting())  	{ -		if (status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE) -		{ //timeout or service unavailable, try again -			llwarns << "Timeout or service unavailable, retrying loadMeshDecomposition() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshDecomposition(mMeshID); -		} -		else +		if (! mProcessed)  		{ -			llassert(status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE); //intentionally trigger a breakpoint -			llwarns << "Unhandled status " << status << llendl; +			LL_WARNS(LOG_MESH) << "Mesh LOD fetch canceled unexpectedly, retrying." << LL_ENDL; +			gMeshRepo.mThread->lockAndLoadMeshLOD(mMeshParams, mLOD);  		} -		return; +		LLMeshRepoThread::decActiveLODRequests();  	} +} -	LLMeshRepository::sBytesReceived += mRequestedBytes; - -	U8* data = NULL; +void LLMeshLODHandler::processFailure(LLCore::HttpStatus status) +{ +	LL_WARNS(LOG_MESH) << "Error during mesh LOD handling.  ID:  " << mMeshParams.getSculptID() +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ").  Not retrying." +					   << LL_ENDL; -	if (data_size > 0) -	{ -		data = new U8[data_size]; -		buffer->readAfter(channels.in(), NULL, data, data_size); -	} +	LLMutexLock lock(gMeshRepo.mThread->mMutex); +	gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD)); +} -	if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) +void LLMeshLODHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ +	if ((! MESH_LOD_PROCESS_FAILED) && gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size))  	{ -		//good fetch from sim, write to VFS for caching -		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); +		// good fetch from sim, write to VFS for caching +		LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE);  		S32 offset = mOffset;  		S32 size = mRequestedBytes;  		if (file.getSize() >= offset+size)  		{ -			LLMeshRepository::sCacheBytesWritten += size;  			file.seek(offset);  			file.write(data, size); +			LLMeshRepository::sCacheBytesWritten += size; +			++LLMeshRepository::sCacheWrites;  		}  	} - -	delete [] data; -} - -void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) -{ -	mProcessed = true; - -	// thread could have already be destroyed during logout -	if( !gMeshRepo.mThread ) -	{ -		return; -	} - -	S32 data_size = buffer->countAfter(channels.in(), NULL); - -	if (status < 200 || status > 400) -	{ -		llwarns << status << ": " << reason << llendl; -	} - -	if (data_size < mRequestedBytes) +	else  	{ -		if (status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE) -		{ //timeout or service unavailable, try again -			llwarns << "Timeout or service unavailable, retrying loadMeshPhysicsShape() for " << mMeshID << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			gMeshRepo.mThread->loadMeshPhysicsShape(mMeshID); -		} -		else -		{ -			llassert(status == HTTP_INTERNAL_ERROR || status == HTTP_SERVICE_UNAVAILABLE); //intentionally trigger a breakpoint -			llwarns << "Unhandled status " << status << llendl; -		} -		return; +		LL_WARNS(LOG_MESH) << "Error during mesh LOD processing.  ID:  " << mMeshParams.getSculptID() +						   << ", Unknown reason.  Not retrying." +						   << LL_ENDL; +		LLMutexLock lock(gMeshRepo.mThread->mMutex); +		gMeshRepo.mThread->mUnavailableQ.push(LLMeshRepoThread::LODRequest(mMeshParams, mLOD));  	} +} -	LLMeshRepository::sBytesReceived += mRequestedBytes; +LLMeshSkinInfoHandler::~LLMeshSkinInfoHandler() +{ +		llassert(mProcessed); +} -	U8* data = NULL; +void LLMeshSkinInfoHandler::processFailure(LLCore::HttpStatus status) +{ +	LL_WARNS(LOG_MESH) << "Error during mesh skin info handling.  ID:  " << mMeshID +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ").  Not retrying." +					   << LL_ENDL; -	if (data_size > 0) -	{ -		data = new U8[data_size]; -		buffer->readAfter(channels.in(), NULL, data, data_size); -	} +	// *TODO:  Mark mesh unavailable on error.  For now, simply leave +	// request unfulfilled rather than retry forever. +} -	if (gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) +void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ +	if ((! MESH_SKIN_INFO_PROCESS_FAILED) && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size))  	{ -		//good fetch from sim, write to VFS for caching +		// good fetch from sim, write to VFS for caching  		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE);  		S32 offset = mOffset; @@ -2166,145 +2905,108 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re  		if (file.getSize() >= offset+size)  		{  			LLMeshRepository::sCacheBytesWritten += size; +			++LLMeshRepository::sCacheWrites;  			file.seek(offset);  			file.write(data, size);  		}  	} - -	delete [] data; +	else +	{ +		LL_WARNS(LOG_MESH) << "Error during mesh skin info processing.  ID:  " << mMeshID +						   << ", Unknown reason.  Not retrying." +						   << LL_ENDL; +		// *TODO:  Mark mesh unavailable on error +	}  } -void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, -							  const LLChannelDescriptors& channels, -							  const LLIOPipe::buffer_ptr_t& buffer) +LLMeshDecompositionHandler::~LLMeshDecompositionHandler()  { -	mProcessed = true; +		llassert(mProcessed); +} -	// thread could have already be destroyed during logout -	if( !gMeshRepo.mThread ) -	{ -		return; -	} +void LLMeshDecompositionHandler::processFailure(LLCore::HttpStatus status) +{ +	LL_WARNS(LOG_MESH) << "Error during mesh decomposition handling.  ID:  " << mMeshID +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ").  Not retrying." +					   << LL_ENDL; +	// *TODO:  Mark mesh unavailable on error.  For now, simply leave +	// request unfulfilled rather than retry forever. +} -	if (status < 200 || status > 400) +void LLMeshDecompositionHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ +	if ((! MESH_DECOMP_PROCESS_FAILED) && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size))  	{ -		//llwarns -		//	<< "Header responder failed with status: " -		//	<< status << ": " << reason << llendl; - -		// 503 (service unavailable) or 499 (internal Linden-generated error) -		// can be due to server load and can be retried - -		// TODO*: Add maximum retry logic, exponential backoff -		// and (somewhat more optional than the others) retries -		// again after some set period of time - -		llassert(status == HTTP_NOT_FOUND || status == HTTP_SERVICE_UNAVAILABLE || status == HTTP_REQUEST_TIME_OUT || status == HTTP_INTERNAL_ERROR); +		// good fetch from sim, write to VFS for caching +		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); -		if (status == HTTP_SERVICE_UNAVAILABLE || status == HTTP_REQUEST_TIME_OUT || status == HTTP_INTERNAL_ERROR) -		{ //retry -			llwarns << "Timeout or service unavailable, retrying." << llendl; -			LLMeshRepository::sHTTPRetryCount++; -			LLMeshRepoThread::HeaderRequest req(mMeshParams); -			LLMutexLock lock(gMeshRepo.mThread->mMutex); -			gMeshRepo.mThread->mHeaderReqQ.push(req); +		S32 offset = mOffset; +		S32 size = mRequestedBytes; -			return; -		} -		else +		if (file.getSize() >= offset+size)  		{ -			llwarns << "Unhandled status: " << status << llendl; +			LLMeshRepository::sCacheBytesWritten += size; +			++LLMeshRepository::sCacheWrites; +			file.seek(offset); +			file.write(data, size);  		}  	} - -	S32 data_size = buffer->countAfter(channels.in(), NULL); - -	U8* data = NULL; - -	if (data_size > 0) +	else  	{ -		data = new U8[data_size]; -		buffer->readAfter(channels.in(), NULL, data, data_size); +		LL_WARNS(LOG_MESH) << "Error during mesh decomposition processing.  ID:  " << mMeshID +						   << ", Unknown reason.  Not retrying." +						   << LL_ENDL; +		// *TODO:  Mark mesh unavailable on error  	} +} -	LLMeshRepository::sBytesReceived += llmin(data_size, 4096); +LLMeshPhysicsShapeHandler::~LLMeshPhysicsShapeHandler() +{ +		llassert(mProcessed); +} -	bool success = gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size); -	 -	llassert(success); +void LLMeshPhysicsShapeHandler::processFailure(LLCore::HttpStatus status) +{ +	LL_WARNS(LOG_MESH) << "Error during mesh physics shape handling.  ID:  " << mMeshID +					   << ", Reason:  " << status.toString() +					   << " (" << status.toTerseString() << ").  Not retrying." +					   << LL_ENDL; +	// *TODO:  Mark mesh unavailable on error +} -	if (!success) -	{ -		llwarns -			<< "Unable to parse mesh header: " -			<< status << ": " << reason << llendl; -	} -	else if (data && data_size > 0) +void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * body, U8 * data, S32 data_size) +{ +	if ((! MESH_PHYS_SHAPE_PROCESS_FAILED) && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size))  	{ -		//header was successfully retrieved from sim, cache in vfs -		LLUUID mesh_id = mMeshParams.getSculptID(); -		LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; +		// good fetch from sim, write to VFS for caching +		LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); -		S32 version = header["version"].asInteger(); +		S32 offset = mOffset; +		S32 size = mRequestedBytes; -		if (version <= MAX_MESH_VERSION) +		if (file.getSize() >= offset+size)  		{ -			std::stringstream str; - -			S32 lod_bytes = 0; - -			for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) -			{ //figure out how many bytes we'll need to reserve in the file -				std::string lod_name = header_lod[i]; -				lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); -			} -		 -			//just in case skin info or decomposition is at the end of the file (which it shouldn't be) -			lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); -			lod_bytes = llmax(lod_bytes, header["physics_convex"]["offset"].asInteger() + header["physics_convex"]["size"].asInteger()); - -			S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; -			S32 bytes = lod_bytes + header_bytes;  - -		 -			//it's possible for the remote asset to have more data than is needed for the local cache -			//only allocate as much space in the VFS as is needed for the local cache -			data_size = llmin(data_size, bytes); - -			LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); -			if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) -			{ -				LLMeshRepository::sCacheBytesWritten += data_size; - -				file.write((const U8*) data, data_size); -			 -				//zero out the rest of the file  -				U8 block[4096]; -				memset(block, 0, 4096); - -				while (bytes-file.tell() > 4096) -				{ -					file.write(block, 4096); -				} - -				S32 remaining = bytes-file.tell(); - -				if (remaining > 0) -				{ -					file.write(block, remaining); -				} -			} +			LLMeshRepository::sCacheBytesWritten += size; +			++LLMeshRepository::sCacheWrites; +			file.seek(offset); +			file.write(data, size);  		}  	} - -	delete [] data; +	else +	{ +		LL_WARNS(LOG_MESH) << "Error during mesh physics shape processing.  ID:  " << mMeshID +						   << ", Unknown reason.  Not retrying." +						   << LL_ENDL; +		// *TODO:  Mark mesh unavailable on error +	}  } -  LLMeshRepository::LLMeshRepository()  : mMeshMutex(NULL),    mMeshThreadCount(0), -  mThread(NULL) +  mThread(NULL), +  mGetMeshVersion(2)  {  } @@ -2323,7 +3025,7 @@ void LLMeshRepository::init()  		apr_sleep(100);  	} -	 +	metrics_teleport_started_signal = LLViewerMessage::getInstance()->setTeleportStartedCallback(teleport_started);  	mThread = new LLMeshRepoThread();  	mThread->start(); @@ -2331,11 +3033,13 @@ void LLMeshRepository::init()  void LLMeshRepository::shutdown()  { -	llinfos << "Shutting down mesh repository." << llendl; +	LL_INFOS(LOG_MESH) << "Shutting down mesh repository." << LL_ENDL; + +	metrics_teleport_started_signal.disconnect();  	for (U32 i = 0; i < mUploads.size(); ++i)  	{ -		llinfos << "Discard the pending mesh uploads " << llendl; +		LL_INFOS(LOG_MESH) << "Discard the pending mesh uploads." << LL_ENDL;  		mUploads[i]->discard() ; //discard the uploading requests.  	} @@ -2350,7 +3054,7 @@ void LLMeshRepository::shutdown()  	for (U32 i = 0; i < mUploads.size(); ++i)  	{ -		llinfos << "Waiting for pending mesh upload " << i << "/" << mUploads.size() << llendl; +		LL_INFOS(LOG_MESH) << "Waiting for pending mesh upload " << (i + 1) << "/" << mUploads.size() << LL_ENDL;  		while (!mUploads[i]->isStopped())  		{  			apr_sleep(10); @@ -2363,7 +3067,7 @@ void LLMeshRepository::shutdown()  	delete mMeshMutex;  	mMeshMutex = NULL; -	llinfos << "Shutting down decomposition system." << llendl; +	LL_INFOS(LOG_MESH) << "Shutting down decomposition system." << LL_ENDL;  	if (mDecompThread)  	{ @@ -2378,6 +3082,9 @@ void LLMeshRepository::shutdown()  //called in the main thread.  S32 LLMeshRepository::update()  { +	// Conditionally log a mesh metrics event +	metricsUpdate(); +	  	if(mUploadWaitList.empty())  	{  		return 0 ; @@ -2397,7 +3104,12 @@ S32 LLMeshRepository::update()  S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)  { -	if (detail < 0 || detail > 4) +	MESH_FASTTIMER_DEFBLOCK; +	 +	// Manage time-to-load metrics for mesh download operations. +	metricsProgress(1); + +	if (detail < 0 || detail >= 4)  	{  		return detail;  	} @@ -2475,9 +3187,32 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para  void LLMeshRepository::notifyLoadedMeshes()  { //called from main thread +	MESH_FASTTIMER_DEFBLOCK; -	LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); - +	if (1 == mGetMeshVersion) +	{ +		// Legacy GetMesh operation with high connection concurrency +		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); +		LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests), +													  REQUEST_HIGH_WATER_MIN, +													  REQUEST_HIGH_WATER_MAX); +		LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, +													 REQUEST_LOW_WATER_MIN, +													 REQUEST_LOW_WATER_MAX); +	} +	else +	{ +		// GetMesh2 operation with keepalives, etc.  With pipelining, +		// we'll increase this. +		LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); +		LLMeshRepoThread::sRequestHighWater = llclamp(5 * S32(LLMeshRepoThread::sMaxConcurrentRequests), +													  REQUEST2_HIGH_WATER_MIN, +													  REQUEST2_HIGH_WATER_MAX); +		LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, +													 REQUEST2_LOW_WATER_MIN, +													 REQUEST2_LOW_WATER_MAX); +	} +	  	//clean up completed upload threads  	for (std::vector<LLMeshUploadThread*>::iterator iter = mUploads.begin(); iter != mUploads.end(); )  	{ @@ -2554,26 +3289,46 @@ void LLMeshRepository::notifyLoadedMeshes()  	//call completed callbacks on finished decompositions  	mDecompThread->notifyCompleted(); -	 -	if (!mThread->mWaiting) -	{ //curl thread is churning, wait for it to go idle -		return; -	} -	static std::string region_name("never name a region this"); +	// For major operations, attempt to get the required locks +	// without blocking and punt if they're not available.  The +	// longest run of holdoffs is kept in sMaxLockHoldoffs just +	// to collect the data.  In testing, I've never seen a value +	// greater than 2 (written to log on exit). +	{ +		LLMutexTrylock lock1(mMeshMutex); +		LLMutexTrylock lock2(mThread->mMutex); -	if (gAgent.getRegion()) -	{ //update capability url  -		if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) +		static U32 hold_offs(0); +		if (! lock1.isLocked() || ! lock2.isLocked())  		{ -			region_name = gAgent.getRegion()->getName(); -			mGetMeshCapability = gAgent.getRegion()->getCapability("GetMesh"); +			// If we can't get the locks, skip and pick this up later. +			++hold_offs; +			sMaxLockHoldoffs = llmax(sMaxLockHoldoffs, hold_offs); +			return; +		} +		hold_offs = 0; +		 +		if (gAgent.getRegion()) +		{ +			// Update capability urls +			static std::string region_name("never name a region this"); +			 +			if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) +			{ +				region_name = gAgent.getRegion()->getName(); +				const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); +				const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh")); +				const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2")); +				mGetMeshVersion = (mesh2.empty() || use_v1) ? 1 : 2; +				mThread->setGetMeshCaps(mesh1, mesh2, mGetMeshVersion); +				LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name +									<< "', GetMesh2:  " << mesh2 +									<< ", GetMesh:  " << mesh1 +									<< ", using version:  " << mGetMeshVersion +									<< LL_ENDL; +			}  		} -	} - -	{ -		LLMutexLock lock1(mMeshMutex); -		LLMutexLock lock2(mThread->mMutex);  		//popup queued error messages from background threads  		while (!mUploadErrorQ.empty()) @@ -2582,47 +3337,55 @@ void LLMeshRepository::notifyLoadedMeshes()  			mUploadErrorQ.pop();  		} -		S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests); - -		if (push_count > 0) +		S32 active_count = LLMeshRepoThread::sActiveHeaderRequests + LLMeshRepoThread::sActiveLODRequests; +		if (active_count < LLMeshRepoThread::sRequestLowWater)  		{ -			//calculate "score" for pending requests - -			//create score map -			std::map<LLUUID, F32> score_map; +			S32 push_count = LLMeshRepoThread::sRequestHighWater - active_count; -			for (U32 i = 0; i < 4; ++i) +			if (mPendingRequests.size() > push_count)  			{ -				for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter) +				// More requests than the high-water limit allows so +				// sort and forward the most important. + +				//calculate "score" for pending requests + +				//create score map +				std::map<LLUUID, F32> score_map; + +				for (U32 i = 0; i < 4; ++i)  				{ -					F32 max_score = 0.f; -					for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) +					for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin();  iter != mLoadingMeshes[i].end(); ++iter)  					{ -						LLViewerObject* object = gObjectList.findObject(*obj_iter); - -						if (object) +						F32 max_score = 0.f; +						for (std::set<LLUUID>::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter)  						{ -							LLDrawable* drawable = object->mDrawable; -							if (drawable) +							LLViewerObject* object = gObjectList.findObject(*obj_iter); + +							if (object)  							{ -								F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); -								max_score = llmax(max_score, cur_score); +								LLDrawable* drawable = object->mDrawable; +								if (drawable) +								{ +									F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); +									max_score = llmax(max_score, cur_score); +								}  							}  						} -					} -					score_map[iter->first.getSculptID()] = max_score; +						score_map[iter->first.getSculptID()] = max_score; +					}  				} -			} -			//set "score" for pending requests -			for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) -			{ -				iter->mScore = score_map[iter->mMeshParams.getSculptID()]; -			} +				//set "score" for pending requests +				for (std::vector<LLMeshRepoThread::LODRequest>::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) +				{ +					iter->mScore = score_map[iter->mMeshParams.getSculptID()]; +				} -			//sort by "score" -			std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); +				//sort by "score" +				std::partial_sort(mPendingRequests.begin(), mPendingRequests.begin() + push_count, +								  mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); +			}  			while (!mPendingRequests.empty() && push_count > 0)  			{ @@ -2676,9 +3439,8 @@ void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info)  				vobj->notifyMeshLoaded();  			}  		} +		mLoadingSkins.erase(info.mMeshID);  	} - -	mLoadingSkins.erase(info.mMeshID);  }  void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) @@ -2687,14 +3449,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom  	if (iter == mDecompositionMap.end())  	{ //just insert decomp into map  		mDecompositionMap[decomp->mMeshID] = decomp; +		mLoadingDecompositions.erase(decomp->mMeshID);  	}  	else  	{ //merge decomp with existing entry  		iter->second->merge(decomp); +		mLoadingDecompositions.erase(decomp->mMeshID);  		delete decomp;  	} - -	mLoadingDecompositions.erase(decomp->mMeshID);  }  void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) @@ -2709,7 +3471,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol  		//make sure target volume is still valid  		if (volume->getNumVolumeFaces() <= 0)  		{ -			llwarns << "Mesh loading returned empty volume." << llendl; +			LL_WARNS(LOG_MESH) << "Mesh loading returned empty volume.  ID:  " << mesh_params.getSculptID() +							   << LL_ENDL;  		}  		{ //update system volume @@ -2722,7 +3485,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol  			}  			else  			{ -				llwarns << "Couldn't find system volume for given mesh." << llendl; +				LL_WARNS(LOG_MESH) << "Couldn't find system volume for mesh " << mesh_params.getSculptID() +								   << LL_ENDL;  			}  		} @@ -2776,6 +3540,8 @@ S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lo  const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const LLVOVolume* requesting_obj)  { +	MESH_FASTTIMER_DEFBLOCK; +  	if (mesh_id.notNull())  	{  		skin_map::iterator iter = mSkinMap.find(mesh_id); @@ -2802,6 +3568,8 @@ const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id, const  void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)  { +	MESH_FASTTIMER_DEFBLOCK; +  	if (mesh_id.notNull())  	{  		LLModel::Decomposition* decomp = NULL; @@ -2819,6 +3587,7 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)  			std::set<LLUUID>::iterator iter = mLoadingPhysicsShapes.find(mesh_id);  			if (iter == mLoadingPhysicsShapes.end())  			{ //no request pending for this skin info +				// *FIXME:  Nothing ever deletes entries, can't be right  				mLoadingPhysicsShapes.insert(mesh_id);  				mPendingPhysicsShapeRequests.push(mesh_id);  			} @@ -2829,6 +3598,8 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id)  LLModel::Decomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id)  { +	MESH_FASTTIMER_DEFBLOCK; +  	LLModel::Decomposition* ret = NULL;  	if (mesh_id.notNull()) @@ -2891,6 +3662,8 @@ bool LLMeshRepository::hasPhysicsShape(const LLUUID& mesh_id)  LLSD& LLMeshRepository::getMeshHeader(const LLUUID& mesh_id)  { +	MESH_FASTTIMER_DEFBLOCK; +  	return mThread->getMeshHeader(mesh_id);  } @@ -2912,7 +3685,7 @@ LLSD& LLMeshRepoThread::getMeshHeader(const LLUUID& mesh_id)  void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures, -									bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload, +								   bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload,  								   LLHandle<LLWholeModelFeeObserver> fee_observer, LLHandle<LLWholeModelUploadObserver> upload_observer)  {  	LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures, upload_skin, upload_joints, upload_url,  @@ -2941,7 +3714,6 @@ S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)  	}  	return -1; -  }  void LLMeshUploadThread::decomposeMeshMatrix(LLMatrix4& transformation, @@ -3206,7 +3978,7 @@ void LLPhysicsDecomp::setMeshData(LLCDMeshData& mesh, bool vertex_based)  		if (ret)  		{ -			llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; +			LL_ERRS(LOG_MESH) << "Convex Decomposition thread valid but could not set mesh data." << LL_ENDL;  		}  	}  } @@ -3282,7 +4054,8 @@ void LLPhysicsDecomp::doDecomposition()  	if (ret)  	{ -		llwarns << "Convex Decomposition thread valid but could not execute stage " << stage << llendl; +		LL_WARNS(LOG_MESH) << "Convex Decomposition thread valid but could not execute stage " << stage << "." +						   << LL_ENDL;  		LLMutexLock lock(mMutex);  		mCurRequest->mHull.clear(); @@ -3411,9 +4184,9 @@ void LLPhysicsDecomp::doDecompositionSingleHull()  	setMeshData(mesh, true);  	LLCDResult ret = decomp->buildSingleHull() ; -	if(ret) +	if (ret)  	{ -		llwarns << "Could not execute decomposition stage when attempting to create single hull." << llendl; +		LL_WARNS(LOG_MESH) << "Could not execute decomposition stage when attempting to create single hull." << LL_ENDL;  		make_box(mCurRequest);  	}  	else @@ -3718,3 +4491,63 @@ bool LLMeshRepository::meshRezEnabled()  	}  	return false;  } + +// Threading:  main thread only +// static +void LLMeshRepository::metricsStart() +{ +	++metrics_teleport_start_count; +	sQuiescentTimer.start(0); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsStop() +{ +	sQuiescentTimer.stop(0); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsProgress(unsigned int this_count) +{ +	static bool first_start(true); + +	if (first_start) +	{ +		metricsStart(); +		first_start = false; +	} +	sQuiescentTimer.ringBell(0, this_count); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsUpdate() +{ +	F64 started, stopped; +	U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0)); +	 +	if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu)) +	{ +		LLSD metrics; + +		metrics["reason"] = "Mesh Download Quiescent"; +		metrics["scope"] = metrics_teleport_start_count > 1 ? "Teleport" : "Login"; +		metrics["start"] = started; +		metrics["stop"] = stopped; +		metrics["fetches"] = LLSD::Integer(total_count); +		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); +		metrics["user_cpu"] = double(user_cpu) / 1.0e6; +		metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; +		LL_INFOS(LOG_MESH) << "EventMarker " << metrics << LL_ENDL; +	} +} + +// Threading:  main thread only +// static +void teleport_started() +{ +	LLMeshRepository::metricsStart(); +} + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 8eaf691d6f..39280bea3a 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2001&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2013, 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 @@ -32,6 +32,12 @@  #include "lluuid.h"  #include "llviewertexture.h"  #include "llvolume.h" +#include "lldeadmantimer.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h"  #define LLCONVEXDECOMPINTER_STATIC 1 @@ -39,8 +45,6 @@  #include "lluploadfloaterobservers.h"  class LLVOVolume; -class LLMeshResponder; -class LLCurlRequest;  class LLMutex;  class LLCondition;  class LLVFS; @@ -215,17 +219,17 @@ class LLMeshRepoThread : public LLThread  {  public: -	static S32 sActiveHeaderRequests; -	static S32 sActiveLODRequests; +	volatile static S32 sActiveHeaderRequests; +	volatile static S32 sActiveLODRequests;  	static U32 sMaxConcurrentRequests; +	static S32 sRequestLowWater; +	static S32 sRequestHighWater; +	static S32 sRequestWaterLevel;			// Stats-use only, may read outside of thread -	LLCurlRequest* mCurlRequest;  	LLMutex*	mMutex;  	LLMutex*	mHeaderMutex;  	LLCondition* mSignal; -	bool mWaiting; -  	//map of known mesh headers  	typedef std::map<LLUUID, LLSD> mesh_header_map;  	mesh_header_map mMeshHeader; @@ -287,8 +291,8 @@ public:  	//set of requested skin info  	std::set<LLUUID> mSkinRequests; -	//queue of completed skin info requests -	std::queue<LLMeshSkinInfo> mSkinInfoQ; +	// list of completed skin info requests +	std::list<LLMeshSkinInfo> mSkinInfoQ;  	//set of requested decompositions  	std::set<LLUUID> mDecompositionRequests; @@ -296,8 +300,8 @@ public:  	//set of requested physics shapes  	std::set<LLUUID> mPhysicsShapeRequests; -	//queue of completed Decomposition info requests -	std::queue<LLModel::Decomposition*> mDecompositionQ; +	// list of completed Decomposition info requests +	std::list<LLModel::Decomposition*> mDecompositionQ;  	//queue of requested headers  	std::queue<HeaderRequest> mHeaderReqQ; @@ -315,7 +319,23 @@ public:  	typedef std::map<LLVolumeParams, std::vector<S32> > pending_lod_map;  	pending_lod_map mPendingLOD; -	static std::string constructUrl(LLUUID mesh_id); +	// llcorehttp library interface objects. +	LLCore::HttpStatus					mHttpStatus; +	LLCore::HttpRequest *				mHttpRequest; +	LLCore::HttpOptions *				mHttpOptions; +	LLCore::HttpOptions *				mHttpLargeOptions; +	LLCore::HttpHeaders *				mHttpHeaders; +	LLCore::HttpRequest::policy_t		mHttpPolicyClass; +	LLCore::HttpRequest::policy_t		mHttpLegacyPolicyClass; +	LLCore::HttpRequest::policy_t		mHttpLargePolicyClass; +	LLCore::HttpRequest::priority_t		mHttpPriority; + +	typedef std::set<LLCore::HttpHandler *> http_request_set; +	http_request_set					mHttpRequestSet;			// Outstanding HTTP requests + +	std::string mGetMeshCapability; +	std::string mGetMesh2Capability; +	int mGetMeshVersion;  	LLMeshRepoThread();  	~LLMeshRepoThread(); @@ -325,8 +345,8 @@ public:  	void lockAndLoadMeshLOD(const LLVolumeParams& mesh_params, S32 lod);  	void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); -	bool fetchMeshHeader(const LLVolumeParams& mesh_params, U32& count); -	bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, U32& count); +	bool fetchMeshHeader(const LLVolumeParams& mesh_params); +	bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod);  	bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size);  	bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size);  	bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); @@ -358,9 +378,37 @@ public:  	static void incActiveHeaderRequests();  	static void decActiveHeaderRequests(); +	// Set the caps strings and preferred version for constructing +	// mesh fetch URLs. +	// +	// Mutex:  must be holding mMutex when called +	void setGetMeshCaps(const std::string & get_mesh1, +						const std::string & get_mesh2, +						int pref_version); + +	// Mutex:  acquires mMutex +	void constructUrl(LLUUID mesh_id, std::string * url, int * version); + +private: +	// Issue a GET request to a URL with 'Range' header using +	// the correct policy class and other attributes.  If an invalid +	// handle is returned, the request failed and caller must retry +	// or dispose of handler. +	// +	// Threads:  Repo thread only +	LLCore::HttpHandle getByteRange(const std::string & url, int cap_version, +									size_t offset, size_t len,  +									LLCore::HttpHandler * handler);  }; -class LLMeshUploadThread : public LLThread  + +// Class whose instances represent a single upload-type request for +// meshes:  one fee query or one actual upload attempt.  Yes, it creates +// a unique thread for that single request.  As it is 1:1, it can also +// trivially serve as the HttpHandler object for request completion +// notifications. + +class LLMeshUploadThread : public LLThread, public LLCore::HttpHandler   {  private:  	S32 mMeshUploadTimeOut ; //maximum time in seconds to execute an uploading request. @@ -381,44 +429,41 @@ public:  	};  	LLPointer<DecompRequest> mFinalDecomp; -	bool mPhysicsComplete; +	volatile bool	mPhysicsComplete;  	typedef std::map<LLPointer<LLModel>, std::vector<LLVector3> > hull_map; -	hull_map mHullMap; +	hull_map		mHullMap;  	typedef std::vector<LLModelInstance> instance_list; -	instance_list mInstanceList; +	instance_list	mInstanceList;  	typedef std::map<LLPointer<LLModel>, instance_list> instance_map; -	instance_map mInstance; +	instance_map	mInstance; -	LLMutex*					mMutex; -	LLCurlRequest* mCurlRequest; +	LLMutex*		mMutex;  	S32				mPendingUploads;  	LLVector3		mOrigin;  	bool			mFinished;	  	bool			mUploadTextures;  	bool			mUploadSkin;  	bool			mUploadJoints; -	BOOL            mDiscarded ; +	volatile bool	mDiscarded;  	LLHost			mHost;  	std::string		mWholeModelFeeCapability;  	std::string		mWholeModelUploadURL;  	LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures, -			bool upload_skin, bool upload_joints, std::string upload_url, bool do_upload = true, -					   LLHandle<LLWholeModelFeeObserver> fee_observer= (LLHandle<LLWholeModelFeeObserver>()), LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>())); +					   bool upload_skin, bool upload_joints, const std::string & upload_url, bool do_upload = true, +					   LLHandle<LLWholeModelFeeObserver> fee_observer = (LLHandle<LLWholeModelFeeObserver>()), +					   LLHandle<LLWholeModelUploadObserver> upload_observer = (LLHandle<LLWholeModelUploadObserver>()));  	~LLMeshUploadThread(); -	void startRequest() { ++mPendingUploads; } -	void stopRequest() { --mPendingUploads; } - -	bool finished() { return mFinished; } +	bool finished() const { return mFinished; }  	virtual void run();  	void preStart();  	void discard() ; -	BOOL isDiscarded(); +	bool isDiscarded() const;  	void generateHulls(); @@ -435,11 +480,23 @@ public:  	void setFeeObserverHandle(LLHandle<LLWholeModelFeeObserver> observer_handle) { mFeeObserverHandle = observer_handle; }  	void setUploadObserverHandle(LLHandle<LLWholeModelUploadObserver> observer_handle) { mUploadObserverHandle = observer_handle; } +	// Inherited from LLCore::HttpHandler +	virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); +  private:  	LLHandle<LLWholeModelFeeObserver> mFeeObserverHandle;  	LLHandle<LLWholeModelUploadObserver> mUploadObserverHandle;  	bool mDoUpload; // if FALSE only model data will be requested, otherwise the model will be uploaded +	LLSD mModelData; +	 +	// llcorehttp library interface objects. +	LLCore::HttpStatus					mHttpStatus; +	LLCore::HttpRequest *				mHttpRequest; +	LLCore::HttpOptions *				mHttpOptions; +	LLCore::HttpHeaders *				mHttpHeaders; +	LLCore::HttpRequest::policy_t		mHttpPolicyClass; +	LLCore::HttpRequest::priority_t		mHttpPriority;  };  class LLMeshRepository @@ -448,21 +505,28 @@ public:  	//metrics  	static U32 sBytesReceived; -	static U32 sHTTPRequestCount; -	static U32 sHTTPRetryCount; +	static U32 sMeshRequestCount;				// Total request count, http or cached, all component types +	static U32 sHTTPRequestCount;				// Http GETs issued (not large) +	static U32 sHTTPLargeRequestCount;			// Http GETs issued for large requests +	static U32 sHTTPRetryCount;					// Total request retries whether successful or failed +	static U32 sHTTPErrorCount;					// Requests ending in error  	static U32 sLODPending;  	static U32 sLODProcessing;  	static U32 sCacheBytesRead;  	static U32 sCacheBytesWritten; -	static U32 sPeakKbps; +	static U32 sCacheReads;						 +	static U32 sCacheWrites; +	static U32 sMaxLockHoldoffs;				// Maximum sequential locking failures +	static LLDeadmanTimer sQuiescentTimer;		// Time-to-complete-mesh-downloads after significant events +  	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);  	LLMeshRepository();  	void init();  	void shutdown(); -	S32 update() ; +	S32 update();  	//mesh management functions  	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); @@ -495,6 +559,12 @@ public:  	S32 getMeshSize(const LLUUID& mesh_id, S32 lod); +	// Quiescent timer management, main thread only. +	static void metricsStart(); +	static void metricsStop(); +	static void metricsProgress(unsigned int count); +	static void metricsUpdate(); +	  	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;  	mesh_load_map mLoadingMeshes[4]; @@ -556,8 +626,7 @@ public:  	void uploadError(LLSD& args);  	void updateInventory(inventory_data data); -	std::string mGetMeshCapability; - +	int mGetMeshVersion;		// Shadows value in LLMeshRepoThread  };  extern LLMeshRepository gMeshRepo; diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index eb6591eb39..32b168b8c5 100755 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -140,7 +140,7 @@ BOOL LLFloaterMove::postBuild()  	initMovementMode(); -	LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(LLFloaterMove::sUpdateFlyingStatus); +	gAgent.addParcelChangedCallback(LLFloaterMove::sUpdateFlyingStatus);  	return TRUE;  } diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 6c2a01fc82..8bb3ace2d9 100755 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -251,7 +251,7 @@ LLPanelPlaces::LLPanelPlaces()  	gInventory.addObserver(mInventoryObserver); -	mAgentParcelChangedConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback( +	mAgentParcelChangedConnection = gAgent.addParcelChangedCallback(  			boost::bind(&LLPanelPlaces::updateVerbs, this));  	//buildFromFile( "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder() diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 0756faf5c0..9c380f63bd 100755 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -359,6 +359,11 @@ void LLTeleportHistoryPanel::ContextMenu::onInfo()  void LLTeleportHistoryPanel::ContextMenu::gotSLURLCallback(const std::string& slurl)  {  	LLClipboard::instance().copyToClipboard(utf8str_to_wstring(slurl),0,slurl.size()); + +	LLSD args; +	args["SLURL"] = slurl; + +	LLNotificationsUtil::add("CopySLURL", args);  }  void LLTeleportHistoryPanel::ContextMenu::onCopyToClipboard() diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp index 9dd665198f..0d09f0bbfc 100755 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -166,7 +166,7 @@ BOOL LLPanelTopInfoBar::postBuild()  		mShowCoordsCtrlConnection = ctrl->getSignal()->connect(boost::bind(&LLPanelTopInfoBar::onNavBarShowParcelPropertiesCtrlChanged, this));  	} -	mParcelMgrConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback( +	mParcelMgrConnection = gAgent.addParcelChangedCallback(  			boost::bind(&LLPanelTopInfoBar::onAgentParcelChange, this));  	setVisibleCallback(boost::bind(&LLPanelTopInfoBar::onVisibilityChange, this, _2)); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index def26b3885..e5f2ca7e5c 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2000&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -1552,7 +1552,7 @@ bool LLTextureFetchWorker::doWork(S32 param)  				else  				{  					llinfos << "HTTP GET failed for: " << mUrl -							<< " Status: " << mGetStatus.toHex() +							<< " Status: " << mGetStatus.toTerseString()  							<< " Reason: '" << mGetReason << "'"  							<< llendl;  				} @@ -1896,7 +1896,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe  	LLCore::HttpStatus status(response->getStatus());  	LL_DEBUGS("Texture") << "HTTP COMPLETE: " << mID -			 << " status: " << status.toHex() +			 << " status: " << status.toTerseString()  			 << " '" << status.toString() << "'"  			 << llendl;  //	unsigned int offset(0), length(0), full_length(0); @@ -1912,7 +1912,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe  		success = false;  		std::string reason(status.toString());  		setGetStatus(status, reason); -		llwarns << "CURL GET FAILED, status: " << status.toHex() +		llwarns << "CURL GET FAILED, status: " << status.toTerseString()  				<< " reason: " << reason << llendl;  	}  	else @@ -2376,6 +2376,7 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image  	  mQAMode(qa_mode),  	  mHttpRequest(NULL),  	  mHttpOptions(NULL), +	  mHttpOptionsWithHeaders(NULL),  	  mHttpHeaders(NULL),  	  mHttpMetricsHeaders(NULL),  	  mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), @@ -2406,11 +2407,13 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image  	mHttpRequest = new LLCore::HttpRequest;  	mHttpOptions = new LLCore::HttpOptions; +	mHttpOptionsWithHeaders = new LLCore::HttpOptions; +	mHttpOptionsWithHeaders->setWantHeaders(true);  	mHttpHeaders = new LLCore::HttpHeaders; -	mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); +	mHttpHeaders->append("Accept", "image/x-j2c");  	mHttpMetricsHeaders = new LLCore::HttpHeaders; -	mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); -	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); +	mHttpMetricsHeaders->append("Content-Type", "application/llsd+xml"); +	mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicy(LLAppCoreHttp::AP_TEXTURE);  }  LLTextureFetch::~LLTextureFetch() @@ -2430,6 +2433,12 @@ LLTextureFetch::~LLTextureFetch()  		mHttpOptions = NULL;  	} +	if (mHttpOptionsWithHeaders) +	{ +		mHttpOptionsWithHeaders->release(); +		mHttpOptionsWithHeaders = NULL; +	} +  	if (mHttpHeaders)  	{  		mHttpHeaders->release(); @@ -3772,7 +3781,7 @@ public:  		else  		{  			LL_WARNS("Texture") << "Error delivering asset metrics to grid.  Status:  " -								<< status.toHex() +								<< status.toTerseString()  								<< ", Reason:  " << status.toString() << LL_ENDL;  		}  	} @@ -4041,7 +4050,7 @@ void LLTextureFetchDebugger::init()  	if (! mHttpHeaders)  	{  		mHttpHeaders = new LLCore::HttpHeaders; -		mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); +		mHttpHeaders->append("Accept", "image/x-j2c");  	}  } @@ -4461,7 +4470,7 @@ S32 LLTextureFetchDebugger::fillCurlQueue()  			LL_WARNS("Texture") << "Couldn't issue HTTP request in debugger for texture "  								<< mFetchingHistory[i].mID -								<< ", status: " << status.toHex() +								<< ", status: " << status.toTerseString()  								<< " reason:  " << status.toString()  								<< LL_ENDL;  			mFetchingHistory[i].mCurlState = FetchEntry::CURL_DONE; @@ -4854,7 +4863,7 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon  	else //failed  	{  		llinfos << "Fetch Debugger : CURL GET FAILED,  ID = " << fetch.mID -				<< ", status: " << status.toHex() +				<< ", status: " << status.toTerseString()  				<< " reason:  " << status.toString() << llendl;  	}  } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 902a3d7a25..3c79a5a24d 100755 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2000&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -351,6 +351,7 @@ private:  	// LLCurl interfaces used in the past.  	LLCore::HttpRequest *				mHttpRequest;					// Ttf  	LLCore::HttpOptions *				mHttpOptions;					// Ttf +	LLCore::HttpOptions *				mHttpOptionsWithHeaders;		// Ttf  	LLCore::HttpHeaders *				mHttpHeaders;					// Ttf  	LLCore::HttpHeaders *				mHttpMetricsHeaders;			// Ttf  	LLCore::HttpRequest::policy_t		mHttpPolicyClass;				// T* diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index e80136b286..50edbb61a8 100755 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2001&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. + * Copyright (C) 2012-2013, 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 @@ -49,6 +49,7 @@  #include "llviewertexturelist.h"  #include "llvovolume.h"  #include "llviewerstats.h" +#include "llmeshrepository.h"  // For avatar texture view  #include "llvoavatarself.h" @@ -517,6 +518,8 @@ void LLGLTexMemBar::draw()  	F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024);  	F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024);  	U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests(); +	F32 x_right = 0.0; +	  	//----------------------------------------------------------------------------  	LLGLSUIDefault gls_ui;  	LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); @@ -543,7 +546,7 @@ void LLGLTexMemBar::draw()  					cache_max_usage);  	//, cache_entries, cache_max_entries -	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, +	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*5,  											 text_color, LLFontGL::LEFT, LLFontGL::TOP);  	U32 cache_read(0U), cache_write(0U), res_wait(0U); @@ -557,13 +560,12 @@ void LLGLTexMemBar::draw()  					cache_write,  					res_wait); -	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, +	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4,  											 text_color, LLFontGL::LEFT, LLFontGL::TOP); -	S32 left = 0 ;  	//---------------------------------------------------------------------------- -	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d", +	text = llformat("Textures: %d Fetch: %d(%d) Pkts:%d(%d) Cache R/W: %d/%d LFS:%d RAW:%d HTP:%d DEC:%d CRE:%d ",  					gTextureList.getNumImages(),  					LLAppViewer::getTextureFetch()->getNumRequests(), LLAppViewer::getTextureFetch()->getNumDeletes(),  					LLAppViewer::getTextureFetch()->mPacketCount, LLAppViewer::getTextureFetch()->mBadPacketCount,  @@ -574,19 +576,30 @@ void LLGLTexMemBar::draw()  					LLAppViewer::getImageDecodeThread()->getPending(),   					gTextureList.mCreateTextureList.size()); -	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2, -									 text_color, LLFontGL::LEFT, LLFontGL::TOP); - +	x_right = 550.0; +	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, +											 text_color, LLFontGL::LEFT, LLFontGL::TOP, +											 LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, +											 &x_right, FALSE); -	left = 550;  	F32 bandwidth = LLAppViewer::getTextureFetch()->getTextureBandwidth();  	F32 max_bandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); -	color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth*.75f ? LLColor4::yellow : text_color; +	color = bandwidth > max_bandwidth ? LLColor4::red : bandwidth > max_bandwidth * .75f ? LLColor4::yellow : text_color;  	color[VALPHA] = text_color[VALPHA]; -	text = llformat("BW:%.0f/%.0f",bandwidth, max_bandwidth); -	LLFontGL::getFontMonospace()->renderUTF8(text, 0, left, v_offset + line_height*2, +	text = llformat("BW:%.0f/%.0f", bandwidth, max_bandwidth); +	LLFontGL::getFontMonospace()->renderUTF8(text, 0, x_right, v_offset + line_height*3,  											 color, LLFontGL::LEFT, LLFontGL::TOP); -	 + +	// Mesh status line +	text = llformat("Mesh: Reqs(Tot/Htp/Big): %u/%u/%u Rtr/Err: %u/%u Cread/Cwrite: %u/%u Low/At/High: %d/%d/%d", +					LLMeshRepository::sMeshRequestCount, LLMeshRepository::sHTTPRequestCount, LLMeshRepository::sHTTPLargeRequestCount, +					LLMeshRepository::sHTTPRetryCount, LLMeshRepository::sHTTPErrorCount, +					LLMeshRepository::sCacheReads, LLMeshRepository::sCacheWrites, +					LLMeshRepoThread::sRequestLowWater, LLMeshRepoThread::sRequestWaterLevel, LLMeshRepoThread::sRequestHighWater); +	LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*2, +											 text_color, LLFontGL::LEFT, LLFontGL::TOP); + +	// Header for texture table columns  	S32 dx1 = 0;  	if (LLAppViewer::getTextureFetch()->mDebugPause)  	{ @@ -629,7 +642,7 @@ BOOL LLGLTexMemBar::handleMouseDown(S32 x, S32 y, MASK mask)  LLRect LLGLTexMemBar::getRequiredRect()  {  	LLRect rect; -	rect.mTop = 50; //LLFontGL::getFontMonospace()->getLineHeight() * 6; +	rect.mTop = 68; //LLFontGL::getFontMonospace()->getLineHeight() * 6;  	return rect;  } diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp index cbd16e873d..73ceb783b5 100755 --- a/indra/newview/lltracker.cpp +++ b/indra/newview/lltracker.cpp @@ -167,6 +167,7 @@ void LLTracker::render3D()  	}  	static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white); +	static LLUIColor map_track_color_under = LLUIColorTable::instance().getColor("MapTrackColorUnder", LLColor4::white);  	// Arbitary location beacon  	if( instance()->mIsTrackingLocation ) @@ -187,7 +188,7 @@ void LLTracker::render3D()  		}  		else  		{ -			renderBeacon( instance()->mTrackedPositionGlobal, map_track_color,  +			renderBeacon( instance()->mTrackedPositionGlobal, map_track_color, map_track_color_under,  					  	instance()->mBeaconText, instance()->mTrackedLocationName );  		}  	} @@ -229,7 +230,7 @@ void LLTracker::render3D()  					// and back again  					instance()->mHasReachedLandmark = FALSE;  				} -				renderBeacon( instance()->mTrackedPositionGlobal, map_track_color,  +				renderBeacon( instance()->mTrackedPositionGlobal, map_track_color, map_track_color_under,  							  instance()->mBeaconText, instance()->mTrackedLandmarkName );  			}  		} @@ -258,7 +259,7 @@ void LLTracker::render3D()  			}  			else  			{ -				renderBeacon( av_tracker.getGlobalPos(), map_track_color,  +				renderBeacon( av_tracker.getGlobalPos(), map_track_color, map_track_color_under,  						  	instance()->mBeaconText, av_tracker.getName() );  			}  		} @@ -412,7 +413,7 @@ const std::string& LLTracker::getTrackedLocationName()  	return instance()->mTrackedLocationName;  } -F32 pulse_func(F32 t, F32 z) +F32 pulse_func(F32 t, F32 z, bool tracking_avatar, std::string direction)  {  	if (!LLTracker::sCheesyBeacon)  	{ @@ -420,8 +421,15 @@ F32 pulse_func(F32 t, F32 z)  	}  	t *= F_PI; -	z -= t*64.f - 256.f; -	 +	if ("DOWN" == direction) +	{ +		z += t*64.f - 256.f; +	} +	else +	{ +		z -= t*64.f - 256.f; +	} +  	F32 a = cosf(z*F_PI/512.f)*10.0f;  	a = llmax(a, 9.9f);  	a -= 9.9f; @@ -474,10 +482,78 @@ void draw_shockwave(F32 center_z, F32 t, S32 steps, LLColor4 color)  	gGL.end();  } +void LLTracker::drawBeacon(LLVector3 pos_agent, std::string direction, LLColor4 fogged_color, F32 dist) +{ +	const U32 BEACON_VERTS = 256; +	F32 step; + +	gGL.matrixMode(LLRender::MM_MODELVIEW); +	gGL.pushMatrix(); + +	if ("DOWN" == direction) +	{ +		gGL.translatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]); +		draw_shockwave(1024.f, gRenderStartTime.getElapsedTimeF32(), 32, fogged_color); +		step = (5020.0f - pos_agent.mV[2]) / BEACON_VERTS; +	} +	else +	{ +		gGL.translatef(pos_agent.mV[0], pos_agent.mV[1], 0); +		step = pos_agent.mV[2] / BEACON_VERTS; +	} + +	gGL.color4fv(fogged_color.mV); + +	LLVector3 x_axis = LLViewerCamera::getInstance()->getLeftAxis(); +	F32 t = gRenderStartTime.getElapsedTimeF32(); + +	for (U32 i = 0; i < BEACON_VERTS; i++) +	{ +		F32 x = x_axis.mV[0]; +		F32 y = x_axis.mV[1]; +			 +		F32 z = i * step; +		F32 z_next = (i+1)*step; + +		bool tracking_avatar = getTrackingStatus() == TRACKING_AVATAR; +		F32 a = pulse_func(t, z, tracking_avatar, direction); +		F32 an = pulse_func(t, z_next, tracking_avatar, direction); + +		LLColor4 c_col = fogged_color + LLColor4(a,a,a,a); +		LLColor4 col_next = fogged_color + LLColor4(an,an,an,an); +		LLColor4 col_edge = fogged_color * LLColor4(a,a,a,0.0f); +		LLColor4 col_edge_next = fogged_color * LLColor4(an,an,an,0.0f); + +		a *= 2.f; +		a += 1.0f; + +		an *= 2.f; +		an += 1.0f; + +		gGL.begin(LLRender::TRIANGLE_STRIP); +		gGL.color4fv(col_edge.mV); +		gGL.vertex3f(-x*a, -y*a, z); +		gGL.color4fv(col_edge_next.mV); +		gGL.vertex3f(-x*an, -y*an, z_next); + +		gGL.color4fv(c_col.mV); +		gGL.vertex3f(0, 0, z); +		gGL.color4fv(col_next.mV); +		gGL.vertex3f(0, 0, z_next); + +		gGL.color4fv(col_edge.mV); +		gGL.vertex3f(x*a,y*a,z); +		gGL.color4fv(col_edge_next.mV); +		gGL.vertex3f(x*an,y*an,z_next); +		gGL.end(); +	} +	gGL.popMatrix(); +}  // static   void LLTracker::renderBeacon(LLVector3d pos_global,  -							 const LLColor4& color,  +							 const LLColor4& color, +							 const LLColor4& color_under,  							 LLHUDText* hud_textp,   							 const std::string& label )  { @@ -497,9 +573,11 @@ void LLTracker::renderBeacon(LLVector3d pos_global,  	}  	LLColor4 fogged_color = color_frac * color + (1 - color_frac)*gSky.getFogColor(); +	LLColor4 fogged_color_under = color_frac * color_under + (1 - color_frac) * gSky.getFogColor();  	F32 FADE_DIST = 3.f;  	fogged_color.mV[3] = llmax(0.2f, llmin(0.5f,(dist-FADE_DIST)/FADE_DIST)); +	fogged_color_under.mV[3] = llmax(0.2f, llmin(0.5f,(dist-FADE_DIST)/FADE_DIST));  	LLVector3 pos_agent = gAgent.getPosAgentFromGlobal(pos_global); @@ -508,64 +586,8 @@ void LLTracker::renderBeacon(LLVector3d pos_global,  	LLGLDisable cull_face(GL_CULL_FACE);  	LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); -	 -	gGL.matrixMode(LLRender::MM_MODELVIEW); -	gGL.pushMatrix(); -	{ -		gGL.translatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]); -		 -		draw_shockwave(1024.f, gRenderStartTime.getElapsedTimeF32(), 32, fogged_color); - -		gGL.color4fv(fogged_color.mV); -		const U32 BEACON_VERTS = 256; -		const F32 step = 1024.0f/BEACON_VERTS; -		 -		LLVector3 x_axis = LLViewerCamera::getInstance()->getLeftAxis(); -		F32 t = gRenderStartTime.getElapsedTimeF32(); -		F32 dr = dist/LLViewerCamera::getInstance()->getFar(); -		 -		for (U32 i = 0; i < BEACON_VERTS; i++) -		{ -			F32 x = x_axis.mV[0]; -			F32 y = x_axis.mV[1]; -			 -			F32 z = i * step; -			F32 z_next = (i+1)*step; -		 -			F32 a = pulse_func(t, z); -			F32 an = pulse_func(t, z_next); -			 -			LLColor4 c_col = fogged_color + LLColor4(a,a,a,a); -			LLColor4 col_next = fogged_color + LLColor4(an,an,an,an); -			LLColor4 col_edge = fogged_color * LLColor4(a,a,a,0.0f); -			LLColor4 col_edge_next = fogged_color * LLColor4(an,an,an,0.0f); -			 -			a *= 2.f; -			a += 1.0f+dr; -			 -			an *= 2.f; -			an += 1.0f+dr; -		 -			gGL.begin(LLRender::TRIANGLE_STRIP); -			gGL.color4fv(col_edge.mV); -			gGL.vertex3f(-x*a, -y*a, z); -			gGL.color4fv(col_edge_next.mV); -			gGL.vertex3f(-x*an, -y*an, z_next); -			 -			gGL.color4fv(c_col.mV); -			gGL.vertex3f(0, 0, z); -			gGL.color4fv(col_next.mV); -			gGL.vertex3f(0, 0, z_next); -			 -			gGL.color4fv(col_edge.mV); -			gGL.vertex3f(x*a,y*a,z); -			gGL.color4fv(col_edge_next.mV); -			gGL.vertex3f(x*an,y*an,z_next); -			 -			gGL.end(); -		} -	} -	gGL.popMatrix(); +	LLTracker::drawBeacon(pos_agent, "DOWN", fogged_color, dist); +	LLTracker::drawBeacon(pos_agent, "UP", fogged_color_under, dist);  	std::string text;  	text = llformat( "%.0f m", to_vec.magVec()); diff --git a/indra/newview/lltracker.h b/indra/newview/lltracker.h index 8e916af315..d8d5803787 100755 --- a/indra/newview/lltracker.h +++ b/indra/newview/lltracker.h @@ -108,8 +108,10 @@ protected:  	LLTracker();  	~LLTracker(); +	static void drawBeacon(LLVector3 pos_agent, std::string direction, LLColor4 fogged_color, F32 dist);  	static void renderBeacon( LLVector3d pos_global,   							 const LLColor4& color,  +							 const LLColor4& color_under,  							 LLHUDText* hud_textp,   							 const std::string& label ); diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 3da934b148..826d296117 100755 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -368,6 +368,7 @@ void init_audio()  		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTyping")));  		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowClose")));  		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowOpen"))); +		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndRestart")));  	}  	audio_update_volume(true); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 3d0eb0c6e7..632a7d8bc3 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -95,6 +95,7 @@  #include "llfloaterproperties.h"  #include "llfloaterregiondebugconsole.h"  #include "llfloaterregioninfo.h" +#include "llfloaterregionrestarting.h"  #include "llfloaterreporter.h"  #include "llfloaterscriptdebug.h"  #include "llfloaterscriptlimits.h" @@ -296,6 +297,7 @@ void LLViewerFloaterReg::registerFloaters()  	LLFloaterReg::add("reset_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterResetQueue>);  	LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionDebugConsole>);  	LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionInfo>); +	LLFloaterReg::add("region_restarting", "floater_region_restarting.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionRestarting>);  	LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebug>);  	LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebugOutput>); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index ac2940fcfc..fb07ab8fbe 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1025,6 +1025,10 @@ U32 info_display_from_string(std::string info_display)  	{  		return LLPipeline::RENDER_DEBUG_AVATAR_VOLUME;  	} +	else if ("joints" == info_display) +	{ +		return LLPipeline::RENDER_DEBUG_AVATAR_JOINTS; +	}  	else if ("raycast" == info_display)  	{  		return LLPipeline::RENDER_DEBUG_RAYCAST; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 3574d37adf..267aa9532c 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -111,6 +111,7 @@  #include "llpanelblockedlist.h"  #include "llpanelplaceprofile.h"  #include "llviewerregion.h" +#include "llfloaterregionrestarting.h"  #include <boost/algorithm/string/split.hpp> //  #include <boost/regex.hpp> @@ -5741,7 +5742,6 @@ bool handle_special_notification(std::string notificationID, LLSD& llsdBlock)  	std::string regionMaturity = LLViewerRegion::accessToString(regionAccess);  	LLStringUtil::toLower(regionMaturity);  	llsdBlock["REGIONMATURITY"] = regionMaturity; -	  	bool returnValue = false;  	LLNotificationPtr maturityLevelNotification;  	std::string notifySuffix = "_Notify"; @@ -5911,6 +5911,7 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem)  			(notificationID == "RegionEntryAccessBlocked") ||  			(notificationID == "LandClaimAccessBlocked") ||  			(notificationID == "LandBuyAccessBlocked") +  		   )  		{  			/*--------------------------------------------------------------------- @@ -5952,7 +5953,41 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem)  			snap_filename += SCREEN_HOME_FILENAME;  			gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, FALSE);  		} -		 + +		if (notificationID == "RegionRestartMinutes" || +			notificationID == "RegionRestartSeconds") +		{ +			S32 seconds; +			if (notificationID == "RegionRestartMinutes") +			{ +				seconds = 60 * static_cast<S32>(llsdBlock["MINUTES"].asInteger()); +			} +			else +			{ +				seconds = static_cast<S32>(llsdBlock["SECONDS"].asInteger()); +			} + +			LLFloaterRegionRestarting* floaterp = LLFloaterReg::findTypedInstance<LLFloaterRegionRestarting>("region_restarting"); + +			if (floaterp) +			{ +				LLFloaterRegionRestarting::updateTime(seconds); +			} +			else +			{ +				LLSD params; +				params["NAME"] = llsdBlock["NAME"]; +				params["SECONDS"] = (LLSD::Integer)seconds; +				LLFloaterRegionRestarting* restarting_floater = dynamic_cast<LLFloaterRegionRestarting*>(LLFloaterReg::showInstance("region_restarting", params)); +				if(restarting_floater) +				{ +					restarting_floater->center(); +				} +			} + +			send_sound_trigger(LLUUID(gSavedSettings.getString("UISndRestart")), 1.0f); +		} +  		LLNotificationsUtil::add(notificationID, llsdBlock);  		return true;  	}	 @@ -6012,7 +6047,6 @@ void process_alert_message(LLMessageSystem *msgsystem, void **user_data)  	std::string message;  	msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, message); -  	process_special_alert_messages(message);  	if (!attempt_standard_notification(msgsystem)) @@ -6036,7 +6070,6 @@ bool handle_not_age_verified_alert(const std::string &pAlertName)  bool handle_special_alerts(const std::string &pAlertName)  {  	bool isHandled = false; -  	if (LLStringUtil::compareStrings(pAlertName, "NotAgeVerified") == 0)  	{ @@ -6072,26 +6105,17 @@ void process_alert_core(const std::string& message, BOOL modal)  		// System message is important, show in upper-right box not tip  		std::string text(message.substr(1));  		LLSD args; -		if (text.substr(0,17) == "RESTART_X_MINUTES") -		{ -			S32 mins = 0; -			LLStringUtil::convertToS32(text.substr(18), mins); -			args["MINUTES"] = llformat("%d",mins); -			LLNotificationsUtil::add("RegionRestartMinutes", args); -		} -		else if (text.substr(0,17) == "RESTART_X_SECONDS") -		{ -			S32 secs = 0; -			LLStringUtil::convertToS32(text.substr(18), secs); -			args["SECONDS"] = llformat("%d",secs); -			LLNotificationsUtil::add("RegionRestartSeconds", args); -		} -		else + +		// *NOTE: If the text from the server ever changes this line will need to be adjusted. +		std::string restart_cancelled = "Region restart cancelled."; +		if (text.substr(0, restart_cancelled.length()) == restart_cancelled)  		{ -			std::string new_msg =LLNotifications::instance().getGlobalString(text); -			args["MESSAGE"] = new_msg; -			LLNotificationsUtil::add("SystemMessage", args); +			LLFloaterRegionRestarting::close();  		} + +		std::string new_msg =LLNotifications::instance().getGlobalString(text); +		args["MESSAGE"] = new_msg; +		LLNotificationsUtil::add("SystemMessage", args);  	}  	else if (modal)  	{ diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 4cdb568d17..e361fad9de 100755 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1580,7 +1580,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use  			// Let interesting parties know about agent parcel change.  			LLViewerParcelMgr* instance = LLViewerParcelMgr::getInstance(); -			instance->mAgentParcelChangedSignal(); +			// Notify anything that wants to know when the agent changes parcels +			gAgent.changeParcels();  			if (instance->mTeleportInProgress)  			{ @@ -2458,10 +2459,6 @@ LLViewerTexture* LLViewerParcelMgr::getPassImage() const  	return sPassImage;  } -boost::signals2::connection LLViewerParcelMgr::addAgentParcelChangedCallback(parcel_changed_callback_t cb) -{ -	return mAgentParcelChangedSignal.connect(cb); -}  /*   * Set finish teleport callback. You can use it to observe all  teleport events.   * NOTE: @@ -2475,7 +2472,7 @@ boost::signals2::connection LLViewerParcelMgr::setTeleportFinishedCallback(telep  	return mTeleportFinishedSignal.connect(cb);  } -boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(parcel_changed_callback_t cb) +boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(teleport_failed_callback_t cb)  {  	return mTeleportFailedSignal.connect(cb);  } diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 6183b7e90e..9da49bb3f3 100755 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -80,8 +80,8 @@ class LLViewerParcelMgr : public LLSingleton<LLViewerParcelMgr>  public:  	typedef boost::function<void (const LLVector3d&, const bool& local)> teleport_finished_callback_t;  	typedef boost::signals2::signal<void (const LLVector3d&, const bool&)> teleport_finished_signal_t; -	typedef boost::function<void()> parcel_changed_callback_t; -	typedef boost::signals2::signal<void()> parcel_changed_signal_t; +	typedef boost::function<void()> teleport_failed_callback_t; +	typedef boost::signals2::signal<void()> teleport_failed_signal_t;  	LLViewerParcelMgr();  	~LLViewerParcelMgr(); @@ -283,9 +283,8 @@ public:  	// the agent is banned or not in the allowed group  	BOOL isCollisionBanned(); -	boost::signals2::connection addAgentParcelChangedCallback(parcel_changed_callback_t cb);  	boost::signals2::connection setTeleportFinishedCallback(teleport_finished_callback_t cb); -	boost::signals2::connection setTeleportFailedCallback(parcel_changed_callback_t cb); +	boost::signals2::connection setTeleportFailedCallback(teleport_failed_callback_t cb);  	void onTeleportFinished(bool local, const LLVector3d& new_pos);  	void onTeleportFailed(); @@ -338,8 +337,7 @@ private:  	BOOL						mTeleportInProgress;  	teleport_finished_signal_t	mTeleportFinishedSignal; -	parcel_changed_signal_t		mTeleportFailedSignal; -	parcel_changed_signal_t		mAgentParcelChangedSignal; +	teleport_failed_signal_t	mTeleportFailedSignal;  	// Array of pieces of parcel edges to potentially draw  	// Has (parcels_per_edge + 1) * (parcels_per_edge + 1) elements so diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index bcc4f46e6f..03e016796e 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2000&license=viewerlgpl$   * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2010-2013, 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 @@ -1603,6 +1603,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	capabilityNames.append("GetDisplayNames");  	capabilityNames.append("GetMesh"); +	capabilityNames.append("GetMesh2");  	capabilityNames.append("GetObjectCost");  	capabilityNames.append("GetObjectPhysicsData");  	capabilityNames.append("GetTexture"); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c3c1edb0a3..1e7d1644b2 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1392,9 +1392,11 @@ void LLVOAvatar::getSpatialExtents(LLVector4a& newMin, LLVector4a& newMax)  //-----------------------------------------------------------------------------  void LLVOAvatar::renderCollisionVolumes()  { +	std::ostringstream ostr;  	for (S32 i = 0; i < mNumCollisionVolumes; i++)  	{  		mCollisionVolumes[i].renderCollision(); +		ostr << mCollisionVolumes[i].getName() << ", ";  	}  	if (mNameText.notNull()) @@ -1403,6 +1405,96 @@ void LLVOAvatar::renderCollisionVolumes()  		mNameText->lineSegmentIntersect(unused, unused, unused, TRUE);  	} + +	mDebugText.clear(); +	addDebugText(ostr.str()); +} + +void LLVOAvatar::renderJoints() +{ +	std::ostringstream ostr; +	std::ostringstream nullstr; + +	for (joint_map_t::iterator iter = mJointMap.begin(); iter != mJointMap.end(); ++iter) +	{ +		LLJoint* jointp = iter->second; +		if (!jointp) +		{ +			nullstr << iter->first << " is NULL" << std::endl; +			continue; +		} + +		ostr << jointp->getName() << ", "; + +		jointp->updateWorldMatrix(); +	 +		gGL.pushMatrix(); +		gGL.multMatrix( &jointp->getXform()->getWorldMatrix().mMatrix[0][0] ); + +		gGL.diffuseColor3f( 1.f, 0.f, 1.f ); +	 +		gGL.begin(LLRender::LINES); +	 +		LLVector3 v[] =  +		{ +			LLVector3(1,0,0), +			LLVector3(-1,0,0), +			LLVector3(0,1,0), +			LLVector3(0,-1,0), + +			LLVector3(0,0,-1), +			LLVector3(0,0,1), +		}; + +		//sides +		gGL.vertex3fv(v[0].mV);  +		gGL.vertex3fv(v[2].mV); + +		gGL.vertex3fv(v[0].mV);  +		gGL.vertex3fv(v[3].mV); + +		gGL.vertex3fv(v[1].mV);  +		gGL.vertex3fv(v[2].mV); + +		gGL.vertex3fv(v[1].mV);  +		gGL.vertex3fv(v[3].mV); + + +		//top +		gGL.vertex3fv(v[0].mV);  +		gGL.vertex3fv(v[4].mV); + +		gGL.vertex3fv(v[1].mV);  +		gGL.vertex3fv(v[4].mV); + +		gGL.vertex3fv(v[2].mV);  +		gGL.vertex3fv(v[4].mV); + +		gGL.vertex3fv(v[3].mV);  +		gGL.vertex3fv(v[4].mV); + + +		//bottom +		gGL.vertex3fv(v[0].mV);  +		gGL.vertex3fv(v[5].mV); + +		gGL.vertex3fv(v[1].mV);  +		gGL.vertex3fv(v[5].mV); + +		gGL.vertex3fv(v[2].mV);  +		gGL.vertex3fv(v[5].mV); + +		gGL.vertex3fv(v[3].mV);  +		gGL.vertex3fv(v[5].mV); + +		gGL.end(); + +		gGL.popMatrix(); +	} + +	mDebugText.clear(); +	addDebugText(ostr.str()); +	addDebugText(nullstr.str());  }  BOOL LLVOAvatar::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, @@ -3077,9 +3169,6 @@ void	LLVOAvatar::forceUpdateVisualMuteSettings()  //------------------------------------------------------------------------  BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  { -	// clear debug text -	mDebugText.clear(); -  	if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))  	{  		S32 central_bake_version = -1; @@ -3588,6 +3677,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)  	{  		setDebugText(mDebugText);  	} +	mDebugText.clear();  	//mesh vertices need to be reskinned  	mNeedsSkin = TRUE; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 9d45a74ecc..0e4121f1c4 100755 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -406,6 +406,7 @@ public:  	F32			getLastSkinTime() { return mLastSkinTime; }  	U32 		renderTransparent(BOOL first_pass);  	void 		renderCollisionVolumes(); +	void		renderJoints();  	static void	deleteCachedImages(bool clearAll=true);  	static void	destroyGL();  	static void	restoreGL(); diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 9aeb2d4978..1c7154d413 100755 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -535,15 +535,16 @@ public:  		RENDER_DEBUG_SHADOW_FRUSTA		= 0x00040000,  		RENDER_DEBUG_SCULPTED           = 0x00080000,  		RENDER_DEBUG_AVATAR_VOLUME      = 0x00100000, -		RENDER_DEBUG_BUILD_QUEUE		= 0x00200000, -		RENDER_DEBUG_AGENT_TARGET       = 0x00400000, -		RENDER_DEBUG_UPDATE_TYPE		= 0x00800000, -		RENDER_DEBUG_PHYSICS_SHAPES     = 0x01000000, -		RENDER_DEBUG_NORMALS	        = 0x02000000, -		RENDER_DEBUG_LOD_INFO	        = 0x04000000, -		RENDER_DEBUG_RENDER_COMPLEXITY  = 0x08000000, -		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x10000000, -		RENDER_DEBUG_TEXEL_DENSITY		= 0x20000000 +		RENDER_DEBUG_AVATAR_JOINTS      = 0x00200000, +		RENDER_DEBUG_BUILD_QUEUE		= 0x00400000, +		RENDER_DEBUG_AGENT_TARGET       = 0x00800000, +		RENDER_DEBUG_UPDATE_TYPE		= 0x01000000, +		RENDER_DEBUG_PHYSICS_SHAPES     = 0x02000000, +		RENDER_DEBUG_NORMALS	        = 0x04000000, +		RENDER_DEBUG_LOD_INFO	        = 0x08000000, +		RENDER_DEBUG_RENDER_COMPLEXITY  = 0x10000000, +		RENDER_DEBUG_ATTACHMENT_BYTES	= 0x20000000, +		RENDER_DEBUG_TEXEL_DENSITY		= 0x40000000  	};  public: diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index f53995732f..3ebb64e3fa 100755 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -122,6 +122,9 @@    <color        name="Blue_80"        value="0 0 1 0.8" /> +  <color +      name="Orange" +      value="1 .82 .46 1" />    <!-- This color name makes potentially unused colors show up bright purple.    Leave this here until all Unused? are removed below, otherwise @@ -511,6 +514,9 @@       name="MapTrackColor"       reference="Red" />      <color +     name="MapTrackColorUnder" +     reference="Blue" /> +    <color       name="MapTrackDisabledColor"       value="0.5 0 0 1" />      <color diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index bb891996c9..94c187e21a 100755 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -153,8 +153,6 @@ with the same filename but different name    <texture name="Command_Speak_Icon"        file_name="toolbar_icons/speak.png"        preload="true" />    <texture name="Command_View_Icon"         file_name="toolbar_icons/view.png"         preload="true" />    <texture name="Command_Voice_Icon"        file_name="toolbar_icons/nearbyvoice.png"  preload="true" /> -  <texture name="Command_Highlighting_Icon" file_name="toolbar_icons/highlighting.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> -  <texture name="Command_Highlighting_Selected_Icon" file_name="toolbar_icons/highlighting_selected.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="Caret_Bottom_Icon"         file_name="toolbar_icons/caret_bottom.png" preload="true" scale.left="1" scale.top="23" scale.right="15" scale.bottom="1" />    <texture name="Caret_Right_Icon"          file_name="toolbar_icons/caret_right.png"  preload="true" scale.left="5" scale.top="15" scale.right="28" scale.bottom="1" />    <texture name="Caret_Left_Icon"           file_name="toolbar_icons/caret_left.png"   preload="true" scale.left="1" scale.top="15" scale.right="23" scale.bottom="1" /> @@ -165,7 +163,6 @@ with the same filename but different name    <texture name="ComboButton_On" file_name="widgets/ComboButton_On.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="ComboButton_Off" file_name="widgets/ComboButton_Off.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> -  <texture name="ComboButton_Hovered" file_name="widgets/ComboButton_Hover.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="Container" file_name="containers/Container.png" preload="false" /> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 43d0f2fb18..7076de55e3 100755 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -210,7 +210,7 @@                       default_tab_group="3"                       tab_group="2"                       name="right_part_holder" -                     min_width="172"> +                     min_width="230">                          <layout_stack                           animate="true"                            default_tab_group="2" diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index 5e92a12251..9fa416012c 100755 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -212,11 +212,11 @@               follows="top|left"               height="20"               layout="topleft" -             left="215" +             left="222"               name="lod_mode_high"               top_delta="0"               visible="false" -             width="135"> +             width="130">                  <item                   name="Triangle Limit"                   value="Triangle Limit" /> @@ -230,7 +230,7 @@               height="20"               increment="10"               layout="topleft" -             left_pad="5" +             left_pad="3"               name="lod_triangle_limit_high"               visible="false"               width="55" /> @@ -342,10 +342,10 @@               follows="top|left"               height="20"               layout="topleft" -             left="215" +             left="222"               name="lod_mode_medium"               top_delta="0" -             width="135"> +             width="130">                  <item                   name="Triangle Limit"                   value="Triangle Limit" /> @@ -359,7 +359,7 @@               height="20"               increment="10"               layout="topleft" -             left_pad="5" +             left_pad="3"               name="lod_triangle_limit_medium"               width="55" />              <spinner @@ -470,10 +470,10 @@               follows="top|left"               height="20"               layout="topleft" -             left="215" +             left="222"               name="lod_mode_low"               top_delta="0" -             width="135"> +             width="130">                  <item                   name="Triangle Limit"                   value="Triangle Limit" /> @@ -487,7 +487,7 @@               height="20"               increment="10"               layout="topleft" -             left_pad="5" +             left_pad="3"               name="lod_triangle_limit_low"               width="55" />              <spinner @@ -598,10 +598,10 @@               follows="top|left"               height="20"               layout="topleft" -             left="215" +             left="222"               name="lod_mode_lowest"               top_delta="0" -             width="135"> +             width="130">                  <item                   name="Triangle Limit"                   value="Triangle Limit" /> @@ -615,7 +615,7 @@               height="20"               increment="10"               layout="topleft" -             left_pad="5" +             left_pad="3"               name="lod_triangle_limit_lowest"               width="55" />              <spinner diff --git a/indra/newview/skins/default/xui/en/floater_region_restarting.xml b/indra/newview/skins/default/xui/en/floater_region_restarting.xml new file mode 100644 index 0000000000..2fe4d0190a --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_region_restarting.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + height="150" + width="290" + layout="topleft" + name="region_restarting" + help_topic="floater_region_restarting" + single_instance="true" + reuse_instance="false" + title="REGION RESTARTING"> +    <string name="RegionName"> +    The region you are in now ([NAME]) is about to restart. + +If you stay in this region you will be logged out. +    </string> +    <string name="RestartSeconds"> +     Seconds until restart +[SECONDS] +    </string> +    <panel +     name="layout_panel_1" +     height="150" +     width="290" +     follows="right|top" +     top="0" +     left="0" +     background_visible="true" +     bg_opaque_color="Orange" +     bg_alpha_color="Orange"> + +    <icon color="1.0 1.0 1.0 1.0" +     tab_stop="false" +     mouse_opaque="false" +     name="icon" +     width="32" +     height="32" +     image_name="notify_caution_icon.tga" +     follows="left|top"> +    </icon> + +    <text +     type="string" +     length="1" +     follows="top|left" +     layout="topleft" +     name="region_name" +     text_color="Black" +     font="SansSerifBold" +     word_wrap="true" +     height="100" +     top="5" +     left="40" +     width="230"> +    The region you are in now (-The longest region name-) is about to restart. + +If you stay in this region you will be logged out. +    </text> +    <text +     type="string" +     length="1" +     follows="top|left" +     layout="topleft" +     name="restart_seconds" +     text_color="Black" +     font="SansSerifLargeBold" +     height="40" +     top="110" +     left="0" +     halign="center" +     width="290"> +     Seconds until restart +     32767 +    </text> +  </panel> + </floater> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index 512205ba43..6fa45d7d66 100755 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -552,6 +552,14 @@           function="Inventory.DoToSelected"           parameter="about" />      </menu_item_call> +   <menu_item_call +     label="Show on Map" +     layout="topleft" +     name="show_on_map"> +        <menu_item_call.on_click +         function="Inventory.DoToSelected" +         parameter="show_on_map" /> +    </menu_item_call>      <menu_item_separator       layout="topleft"        name="Animation Separator" /> diff --git a/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml b/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml index 0160d52b17..f939c3996d 100755 --- a/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml +++ b/indra/newview/skins/default/xui/en/menu_teleport_history_item.xml @@ -17,7 +17,7 @@           function="TeleportHistory.MoreInformation" />      </menu_item_call>      <menu_item_call -     label="Copy to Clipboard" +     label="Copy SLurl"       layout="topleft"       name="CopyToClipboard">          <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index ff022c7d3a..db6d8ff34e 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2564,6 +2564,16 @@             parameter="collision skeleton" />          </menu_item_check>          <menu_item_check +         label="Joints" +         name="Joints"> +          <menu_item_check.on_check +           function="Advanced.CheckInfoDisplay" +           parameter="joints" /> +          <menu_item_check.on_click +           function="Advanced.ToggleInfoDisplay" +           parameter="joints" /> +        </menu_item_check> +        <menu_item_check           label="Raycast"           name="Raycast">            <menu_item_check.on_check diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index f6eb6c06a8..eb8f28b6a2 100755 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6893,8 +6893,8 @@ This will add a bookmark in your inventory so you can quickly IM this Resident.    <notification     icon="notify.tga"     name="RegionRestartMinutes" +   show_toast="false"     priority="high" -   sound="UISndAlert"     type="notify">  The region "[NAME]" will restart in [MINUTES] minutes.  If you stay in this region you will be logged out. @@ -6903,8 +6903,8 @@ If you stay in this region you will be logged out.    <notification     icon="notify.tga"     name="RegionRestartSeconds" +   show_toast="false"     priority="high" -   sound="UISndAlert"     type="notify">  The region "[NAME]" will restart in [SECONDS] seconds.  If you stay in this region you will be logged out. diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 3caf2b3d7e..d2caf63052 100755 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -66,8 +66,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M       tab_position="top"       top="0"       halign="center" -     right="-5" -     use_highlighting_on_hover="true"> +     right="-5">  <!-- ================================= NEARBY tab =========================== --> @@ -507,6 +506,7 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M                  right="-10"                  top_pad="4"                  left="3" +                use_ellipses="true"                  name="groupcount">                You belong to [COUNT] groups, and can join [REMAINING] more.              </text> diff --git a/indra/newview/skins/default/xui/en/widgets/location_input.xml b/indra/newview/skins/default/xui/en/widgets/location_input.xml index 4ea1aa6efb..61ec046649 100755 --- a/indra/newview/skins/default/xui/en/widgets/location_input.xml +++ b/indra/newview/skins/default/xui/en/widgets/location_input.xml @@ -150,7 +150,6 @@    <combo_button  		name="Location History"                  label="" -                image_hover_unselected="ComboButton_Hovered"                  pad_right="0"/>    <combo_list  	      bg_writeable_color="MenuDefaultBgColor" diff --git a/indra/newview/skins/default/xui/en/widgets/tab_container.xml b/indra/newview/skins/default/xui/en/widgets/tab_container.xml index 9559be214a..0586119681 100755 --- a/indra/newview/skins/default/xui/en/widgets/tab_container.xml +++ b/indra/newview/skins/default/xui/en/widgets/tab_container.xml @@ -24,26 +24,17 @@ label_pad_left - padding to the left of tab button labels                 tab_bottom_image_unselected="Toolbar_Left_Off"                 tab_bottom_image_selected="Toolbar_Left_Selected"                 tab_left_image_unselected="SegmentedBtn_Left_Disabled" -               tab_left_image_selected="SegmentedBtn_Left_Selected_Over" -               tab_top_image_hovered="TabTop_Left_Selected" -               tab_button_image_hovered="Toolbar_Left_Selected" -               tab_left_image_hovered="SegmentedBtn_Left_Selected_Over"/> +               tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>    <middle_tab tab_top_image_unselected="TabTop_Middle_Off"                 tab_top_image_selected="TabTop_Middle_Selected"                 tab_bottom_image_unselected="Toolbar_Middle_Off"                 tab_bottom_image_selected="Toolbar_Middle_Selected"                 tab_left_image_unselected="SegmentedBtn_Left_Disabled" -               tab_left_image_selected="SegmentedBtn_Left_Selected_Over" -               tab_top_image_hovered="TabTop_Middle_Selected" -               tab_button_image_hovered="Toolbar_Middle_Selected" -               tab_left_image_hovered="SegmentedBtn_Left_Selected_Over"/> +               tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>    <last_tab tab_top_image_unselected="TabTop_Right_Off"                 tab_top_image_selected="TabTop_Right_Selected"                 tab_bottom_image_unselected="Toolbar_Right_Off"                 tab_bottom_image_selected="Toolbar_Right_Selected"                 tab_left_image_unselected="SegmentedBtn_Left_Disabled" -               tab_left_image_selected="SegmentedBtn_Left_Selected_Over" -               tab_top_image_hovered="TabTop_Right_Selected" -               tab_button_image_hovered="Toolbar_Right_Selected" -               tab_left_image_hovered="SegmentedBtn_Left_Selected_Over"/> +               tab_left_image_selected="SegmentedBtn_Left_Selected_Over"/>  </tab_container> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9a617c2a13..96b4c7268c 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -38,7 +38,7 @@ viewer_dir = os.path.dirname(__file__)  # Put it FIRST because some of our build hosts have an ancient install of  # indra.util.llmanifest under their system Python!  sys.path.insert(0, os.path.join(viewer_dir, os.pardir, "lib", "python")) -from indra.util.llmanifest import LLManifest, main, proper_windows_path, path_ancestors, CHANNEL_VENDOR_BASE, RELEASE_CHANNEL +from indra.util.llmanifest import LLManifest, main, proper_windows_path, path_ancestors, CHANNEL_VENDOR_BASE, RELEASE_CHANNEL, ManifestError  try:      from llbase import llsd  except ImportError: @@ -818,11 +818,27 @@ class Darwin_i386_Manifest(ViewerManifest):                  keychain_pwd = open(keychain_pwd_path).read().rstrip()                  self.run_command('security unlock-keychain -p "%s" "%s/Library/Keychains/viewer.keychain"' % ( keychain_pwd, home_path ) ) -                self.run_command('codesign --verbose --force --keychain "%(home_path)s/Library/Keychains/viewer.keychain" --sign %(identity)r %(bundle)r' % { -                                 'home_path' : home_path, -                                 'identity': identity, -                                 'bundle': self.get_dst_prefix() -                }) +                signed=False +                sign_attempts=3 +                sign_retry_wait=15 +                while (not signed) and (sign_attempts > 0): +                    try: +                        sign_attempts-=1; +                        self.run_command( +                           'codesign --verbose --force --keychain "%(home_path)s/Library/Keychains/viewer.keychain" --sign %(identity)r %(bundle)r' % { +                               'home_path' : home_path, +                               'identity': identity, +                               'bundle': self.get_dst_prefix() +                               }) +                        signed=True # if no exception was raised, the codesign worked +                    except ManifestError, err: +                        if sign_attempts: +                            print >> sys.stderr, "codesign failed, waiting %d seconds before retrying" % sign_retry_wait +                            time.sleep(sign_retry_wait) +                            sign_retry_wait*=2 +                        else: +                            print >> sys.stderr, "Maximum codesign attempts exceeded; giving up" +                            raise          imagename="SecondLife_" + '_'.join(self.args['version']) | 
