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