Merge remote-tracking branch 'refs/remotes/beq-fs/perffloater'

Conflicts:
	indra/newview/llfloaterperformance.cpp
	indra/newview/llfloaterpreference.cpp
	indra/newview/llfloaterpreferencesgraphicsadvanced.cpp
	indra/newview/llnamelistctrl.cpp
	indra/newview/llnamelistctrl.h
	indra/newview/llviewermenu.cpp
	indra/newview/llvoavatar.cpp
	indra/newview/llvoavatar.h
	indra/newview/skins/default/colors.xml
	indra/newview/skins/default/xui/en/menu_attachment_other.xml
	indra/newview/skins/default/xui/en/menu_avatar_other.xml
	indra/newview/skins/default/xui/en/strings.xml
meow-7.2.2
Beq 2021-09-14 12:53:10 +01:00
commit 01c0ecc7f9
20 changed files with 249 additions and 33 deletions

View File

@ -1944,9 +1944,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>dce2cc624e216eb991412ec0c5538cf9</string>
<string>94f8c766d77d68e0b8b1ac783b6a02dc</string>
<key>url</key>
<string>file:///opt/firestorm/kdu-8.1-linux64-210652339.tar.bz2</string>
<string>file:///opt/firestorm/kdu-8.2-linux64-212351600.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
@ -1956,9 +1956,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>1cd284c85b5f05fff6b286e85b55ebf2</string>
<string>e88372fa10b83c6900443dcc12c12338</string>
<key>url</key>
<string>file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2</string>
<string>file:///c:/cygwin/opt/firestorm/kdu-8.2-windows-212351246.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
@ -1968,9 +1968,9 @@
<key>archive</key>
<map>
<key>hash</key>
<string>1cd284c85b5f05fff6b286e85b55ebf2</string>
<string>e88372fa10b83c6900443dcc12c12338</string>
<key>url</key>
<string>file:///c:/cygwin/opt/firestorm/kdu-8.1-windows-210862059.tar.bz2</string>
<string>file:///c:/cygwin/opt/firestorm/kdu-8.2-windows-212351246.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>

View File

@ -28,4 +28,11 @@
namespace FSTelemetry
{
bool active{false};
int RecordSceneTime::writeBuffer{0};
bool RecordSceneTime::collectionEnabled{true};
std::array< typename RecordSceneTime::StatsArray, 2 > RecordSceneTime::stats{ {} };
}

View File

@ -63,9 +63,178 @@
#define FSTelemetryIsConnected
#endif // TRACY_ENABLE
#include <chrono>
#include <array>
#include <unordered_map>
namespace FSTelemetry
{
extern bool active;
}
enum class ObjStatType_t{
RENDER_GEOMETRY=0,
RENDER_SHADOWS,
RENDER_COMBINED,
STATS_COUNT
};
enum class SceneStatType_t{
RENDER_GEOMETRY=0,
RENDER_SHADOWS,
RENDER_HUDS,
RENDER_UI,
RENDER_COMBINED,
RENDER_SWAP,
RENDER_FRAME,
RENDER_SLEEP,
RENDER_LFS,
RENDER_MESHREPO,
RENDER_FPSLIMIT,
RENDER_FPS,
STATS_COUNT
};
using ObjStatType = ObjStatType_t;
using SceneStatType = SceneStatType_t;
class RecordSceneTime
{
using StatsEnum = SceneStatType;
using StatsArray = std::array<uint64_t, static_cast<size_t>(StatsEnum::STATS_COUNT)>;
// using StatsBlock = std::unordered_map<T, StatsArray>;
static int writeBuffer;
static std::array<StatsArray,2> stats;
static bool collectionEnabled;
RecordSceneTime(const RecordSceneTime&) = delete;
RecordSceneTime() = delete;
const StatsEnum type;
std::chrono::steady_clock::time_point start;
public:
static inline void enable(){collectionEnabled=true;};
static inline void disable(){collectionEnabled=false;};
static inline bool enabled(){return(collectionEnabled);};
RecordSceneTime(SceneStatType type):start{std::chrono::steady_clock::now()}, type{type} {}
~RecordSceneTime()
{
auto val = std::chrono::duration<uint64_t, std::nano>(std::chrono::steady_clock::now() - start).count();
stats[writeBuffer][static_cast<size_t>(type)] += val;
};
static inline void toggleBuffer()
{
if(enabled())
{
// stats[writeBuffer][static_cast<size_t>(SceneStatType::RENDER_FPS)] = LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::FPS,3); // last 3 Frames
writeBuffer = (writeBuffer+1)%2;
}; // not we are relying on atomic updates here. The risk is low and would cause minor errors in the stats display.
auto& statsArray = stats[writeBuffer];
std::fill_n(statsArray.begin() ,static_cast<size_t>(SceneStatType::STATS_COUNT),0);
}
static inline int getReadBufferIndex(){return (writeBuffer+1)%2;};
static inline StatsArray getCurrentStatsBuffer(){ return stats[getReadBufferIndex()];}
static inline uint64_t get(StatsEnum type){return stats[getReadBufferIndex()][static_cast<size_t>(type)];}
};
template <typename T>
class RecordObjectTime
{
using StatsEnum = ObjStatType;
using StatsArray = std::array<uint64_t, static_cast<size_t>(StatsEnum::STATS_COUNT)>;
using StatsBlock = std::unordered_map<T, StatsArray>;
static int writeBuffer;
static std::array<StatsBlock,2> stats;
static std::array<StatsArray,2> max;
static std::array<StatsArray,2> sum;
static bool collectionEnabled;
RecordObjectTime(const RecordObjectTime&) = delete;
RecordObjectTime() = delete;
const T key;
const StatsEnum type;
std::chrono::steady_clock::time_point start;
public:
static inline void enable(){collectionEnabled=true;};
static inline void disable(){collectionEnabled=false;};
static inline bool enabled(){return(collectionEnabled);};
RecordObjectTime(T key, ObjStatType type):start{std::chrono::steady_clock::now()}, key{key}, type{type} {}
~RecordObjectTime()
{
using ST = StatsEnum;
// Note: nullptr is used as the key for global stats
auto val = std::chrono::duration<uint64_t, std::nano>(std::chrono::steady_clock::now() - start).count();
if(key)
{
stats[writeBuffer][key][static_cast<size_t>(type)] += val;
stats[writeBuffer][key][static_cast<size_t>(ST::RENDER_COMBINED)] += val;
if(max[writeBuffer][static_cast<size_t>(type)] < stats[writeBuffer][key][static_cast<size_t>(type)])
{
max[writeBuffer][static_cast<size_t>(type)] = stats[writeBuffer][key][static_cast<size_t>(type)];
}
if(max[writeBuffer][static_cast<size_t>(ST::RENDER_COMBINED)] < stats[writeBuffer][key][static_cast<size_t>(ST::RENDER_COMBINED)])
{
max[writeBuffer][static_cast<size_t>(ST::RENDER_COMBINED)] = stats[writeBuffer][key][static_cast<size_t>(ST::RENDER_COMBINED)];
}
sum[writeBuffer][static_cast<size_t>(type)] += val;
sum[writeBuffer][static_cast<size_t>(ST::RENDER_COMBINED)] += val;
}
};
static inline void toggleBuffer()
{
using ST = StatsEnum;
if(enabled())
{
writeBuffer = (writeBuffer+1)%2;
}; // note we are relying on atomic updates here. The risk is low and would cause minor errors in the stats display.
auto& statsMap = stats[writeBuffer];
for(auto& stat_entry : statsMap)
{
std::fill_n(stat_entry.second.begin() ,static_cast<size_t>(ST::STATS_COUNT),0);
}
statsMap.clear();
std::fill_n(max[writeBuffer].begin(),static_cast<size_t>(ST::STATS_COUNT),0);
std::fill_n(sum[writeBuffer].begin(),static_cast<size_t>(ST::STATS_COUNT),0);
}
static inline int getReadbufferIndex(){return (writeBuffer+1)%2;};
static inline StatsBlock& getCurrentStatsBuffer(){ return stats[(writeBuffer+1)%2]; }
static inline uint64_t getMax(StatsEnum type){return max[(writeBuffer+1)%2][static_cast<size_t>(type)];}
static inline uint64_t getSum(StatsEnum type){return sum[(writeBuffer+1)%2][static_cast<size_t>(type)];}
static inline uint64_t getNum(){return stats[(writeBuffer+1)%2].size();}
static inline uint64_t get(T key, StatsEnum type){return stats[(writeBuffer+1)%2][key][static_cast<size_t>(type)];}
};
static inline void toggleBuffer()
{
// RecordObjectTime<LLVOAvatar*>::toggleBuffer();
RecordSceneTime::toggleBuffer();
}
template< typename T >
int RecordObjectTime<T>::writeBuffer{0};
template< typename T >
bool RecordObjectTime<T>::collectionEnabled{true};
template< typename T >
std::array< typename RecordObjectTime< T >::StatsArray, 2 > RecordObjectTime<T>::max;
template< typename T >
std::array< typename RecordObjectTime< T >::StatsArray, 2 > RecordObjectTime<T>::sum;
template< typename T >
std::array< typename RecordObjectTime< T >::StatsBlock, 2 > RecordObjectTime< T >::stats{ {{}} };
}// namespace FSTelemetry
#endif

View File

@ -321,7 +321,12 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod
// *TODO: This seems to be wrong. The base class should have no idea of
// how j2c compression works so no good way of computing what's the byte
// range to be used.
#if (KDU_MAJOR_VERSION*10000 + KDU_MINOR_VERSION*100 + KDU_PATCH_VERSION) >= 80200
mCodeStreamp->set_max_bytes(max_bytes, false);
#else
mCodeStreamp->set_max_bytes(max_bytes,true);
#endif
// If you want to flip or rotate the image for some reason, change
// the resolution, or identify a restricted region of interest, this is

View File

@ -1012,12 +1012,7 @@ void MediaPluginCEF::checkEditState()
void MediaPluginCEF::setVolume()
{
//mVolumeCatcher.setVolume(mCurVolume);
#if 0//(DULLAHAN_VERSION_MAJOR*100 + DULLAHAN_VERSION_MINOR) >= 108
mCEFLib->setVolume(mCurVolume);
#else
mVolumeCatcher.setVolume(mCurVolume);
#endif
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1182,7 +1182,7 @@ set(viewer_HEADER_FILES
llfloaterperms.h
llfloaterpostprocess.h
llfloaterpreference.h
llfloaterpreferencesgraphicsadvanced.h
# llfloaterpreferencesgraphicsadvanced.h
llfloaterpreferenceviewadvanced.h
llfloaterpreviewtrash.h
llfloaterproperties.h

View File

@ -124,7 +124,7 @@ void FSRadarEntry::processProperties(void* data, EAvatarProcessorType type)
else if (type == APT_NOTES)
{
LLAvatarNotes* avatar_notes = static_cast<LLAvatarNotes*>(data);
if (avatar_notes && avatar_notes->agent_id == gAgentID && avatar_notes->target_id == mID)
if (avatar_notes && avatar_notes->agent_id == gAgentID && avatar_notes->target_id == mID && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
{
mNotes = avatar_notes->notes;
LLStringUtil::trim(mNotes);

View File

@ -1631,6 +1631,9 @@ bool LLAppViewer::frame()
bool LLAppViewer::doFrame()
{
{
FSTelemetry::RecordSceneTime T (FSTelemetry::SceneStatType::RENDER_FRAME);
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
LLSD newFrame;
// <FS:Beq> telemetry enabling.
@ -1828,6 +1831,7 @@ bool LLAppViewer::doFrame()
static LLCachedControl<S32> yield_time(gSavedSettings, "YieldTime", -1);
if(yield_time >= 0)
{
FSTelemetry::RecordSceneTime T ( FSTelemetry::SceneStatType::RENDER_SLEEP );
LL_RECORD_BLOCK_TIME(FTM_YIELD);
ms_sleep(yield_time);
}
@ -1922,6 +1926,7 @@ bool LLAppViewer::doFrame()
if (fsLimitFramerate && LLStartUp::getStartupState() == STATE_STARTED && !gTeleportDisplay && !logoutRequestSent() && max_fps > F_APPROXIMATELY_ZERO)
{
// Sleep a while to limit frame rate.
FSTelemetry::RecordSceneTime T (FSTelemetry::SceneStatType::RENDER_FPSLIMIT);
F32 min_frame_time = 1.f / (F32)max_fps;
S32 milliseconds_to_sleep = llclamp((S32)((min_frame_time - frameTimer.getElapsedTimeF64()) * 1000.f), 0, 1000);
if (milliseconds_to_sleep > 0)
@ -1961,6 +1966,9 @@ bool LLAppViewer::doFrame()
}
FSFrameMark; // <FS:Beq> Tracy support delineate Frame
LLPROFILE_UPDATE();
}
FSTelemetry::RecordSceneTime::toggleBuffer();
FSTelemetry::RecordObjectTime<LLVOAvatar*>::toggleBuffer();
return ! LLApp::isRunning();
}

View File

@ -579,6 +579,8 @@ void LLDrawPoolAvatar::renderShadow(S32 pass)
{
return;
}
FSTelemetry::RecordObjectTime<LLVOAvatar*> T(avatarp, FSTelemetry::ObjStatType::RENDER_SHADOWS);
LLVOAvatar::AvatarOverallAppearance oa = avatarp->getOverallAppearance();
BOOL impostor = !LLPipeline::sImpostorRender && avatarp->isImpostor();
if (oa == LLVOAvatar::AOA_INVISIBLE ||
@ -1494,6 +1496,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass)
{
return;
}
FSTelemetry::RecordObjectTime<LLVOAvatar*> T(avatarp, FSTelemetry::ObjStatType::RENDER_GEOMETRY);
// <FS:Zi> Add avatar hitbox debug
static LLCachedControl<bool> render_hitbox(gSavedSettings, "DebugRenderHitboxes", false);

View File

@ -64,6 +64,7 @@
#include "llnotificationhandler.h"
#include "llnotificationmanager.h"
#include "llnotificationsutil.h"
#include "llviewercontrol.h"
LLFloaterAutoReplaceSettings::LLFloaterAutoReplaceSettings(const LLSD& key)

View File

@ -333,19 +333,21 @@ void LLFloaterPerformance::populateNearbyList()
getNearbyAvatars(valid_nearby_avs);
std::vector<LLCharacter*>::iterator char_iter = valid_nearby_avs.begin();
auto render_max = FSTelemetry::RecordObjectTime<LLVOAvatar*>::getMax(FSTelemetry::ObjStatType::RENDER_COMBINED);
while (char_iter != valid_nearby_avs.end())
{
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(*char_iter);
if (avatar && (LLVOAvatar::AOA_INVISIBLE != avatar->getOverallAppearance()))
{
S32 complexity_short = llmax((S32)avatar->getVisualComplexity() / 1000, 1);;
S32 complexity_short = llmax((S32)avatar->getVisualComplexity() / 1000, 1);
auto render_av = FSTelemetry::RecordObjectTime<LLVOAvatar*>::get(avatar,FSTelemetry::ObjStatType::RENDER_COMBINED);
LLSD item;
item["id"] = avatar->getID();
LLSD& row = item["columns"];
row[0]["column"] = "complex_visual";
row[0]["type"] = "bar";
LLSD& value = row[0]["value"];
value["ratio"] = (F32)complexity_short / mNearbyMaxComplexity * 1000;
value["ratio"] = (double)render_av / render_max;
value["bottom"] = BAR_BOTTOM_PAD;
value["left_pad"] = BAR_LEFT_PAD;
value["right_pad"] = BAR_RIGHT_PAD;
@ -502,24 +504,24 @@ void LLFloaterPerformance::onCustomAction(const LLSD& userdata, const LLUUID& av
{
const std::string command_name = userdata.asString();
S32 new_setting = 0;
LLVOAvatar::VisualMuteSettings new_setting = LLVOAvatar::AV_RENDER_NORMALLY;
if ("default" == command_name)
{
new_setting = S32(LLVOAvatar::AV_RENDER_NORMALLY);
new_setting = LLVOAvatar::AV_RENDER_NORMALLY;
}
else if ("never" == command_name)
{
new_setting = S32(LLVOAvatar::AV_DO_NOT_RENDER);
new_setting = LLVOAvatar::AV_DO_NOT_RENDER;
}
else if ("always" == command_name)
{
new_setting = S32(LLVOAvatar::AV_ALWAYS_RENDER);
new_setting = LLVOAvatar::AV_ALWAYS_RENDER;
}
LLVOAvatar *avatarp = find_avatar(av_id);
if (avatarp)
{
avatarp->setVisualMuteSettings(LLVOAvatar::VisualMuteSettings(new_setting));
avatarp->setVisualMuteSettings(new_setting);
}
else
{
@ -541,19 +543,19 @@ bool LLFloaterPerformance::isActionChecked(const LLSD& userdata, const LLUUID& a
// </FS:Ansariel>
if ("default" == command_name)
{
return (visual_setting == S32(LLVOAvatar::AV_RENDER_NORMALLY));
return (visual_setting == LLVOAvatar::AV_RENDER_NORMALLY);
}
else if ("non_default" == command_name)
{
return (visual_setting != S32(LLVOAvatar::AV_RENDER_NORMALLY));
return (visual_setting != LLVOAvatar::AV_RENDER_NORMALLY);
}
else if ("never" == command_name)
{
return (visual_setting == S32(LLVOAvatar::AV_DO_NOT_RENDER));
return (visual_setting == LLVOAvatar::AV_DO_NOT_RENDER);
}
else if ("always" == command_name)
{
return (visual_setting == S32(LLVOAvatar::AV_ALWAYS_RENDER));
return (visual_setting == LLVOAvatar::AV_ALWAYS_RENDER);
}
return false;
}

View File

@ -167,7 +167,7 @@
// <FS:Zi> FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search.
#include "llfiltereditor.h"
#include "llviewershadermgr.h"
//<FS:HG> FIRE-6340, FIRE-6567 - Setting Bandwidth issues
//const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f;
char const* const VISIBILITY_DEFAULT = "default";
@ -5330,6 +5330,25 @@ BOOL FSPanelPreferenceBackup::postBuild()
getChild<LLLineEditor>("settings_backup_path")->setValue(dir_name);
// </FS:Zi>
// <FS:Beq>
#if !defined OPENSIM
// Note: Windlight setting restore is enabled in OPENSIM bulds irrespective of grid (or pre-login)
// windlights settings folders are not grid specific and thus neither is the restore.
// if windlight folders existsed they will be backed up on all builds but for SL only builds they will not be restored.
LLScrollListCtrl* globalFoldersScrollList = getChild<LLScrollListCtrl>("restore_global_folders_list");
std::vector<LLScrollListItem*> globalFoldersList = globalFoldersScrollList->getAllData();
for (const auto item : globalFoldersList)
{
// if it is windlight related remove it.
if (item->getValue().asString().rfind("windlight",0) == 0)
{
LL_INFOS() << "removing windlight folder (no longer used in SL) : " << item->getValue().asString() << " index: " << globalFoldersScrollList->getItemIndex(item) << LL_ENDL;
globalFoldersScrollList->deleteSingleItem(globalFoldersScrollList->getItemIndex(item));
}
}
#endif
// </FS:Beq>
return LLPanelPreference::postBuild();
}

View File

@ -1192,6 +1192,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
void render_hud_attachments()
{
FSTelemetry::RecordSceneTime T (FSTelemetry::SceneStatType::RENDER_HUDS);
gGL.matrixMode(LLRender::MM_PROJECTION);
gGL.pushMatrix();
gGL.matrixMode(LLRender::MM_MODELVIEW);
@ -1399,6 +1400,7 @@ bool setup_hud_matrices(const LLRect& screen_region)
void render_ui(F32 zoom_factor, int subfield)
{
FSTelemetry::RecordSceneTime T (FSTelemetry::SceneStatType::RENDER_UI);
LL_RECORD_BLOCK_TIME(FTM_RENDER_UI);
LLGLState::checkStates();
@ -1484,6 +1486,7 @@ static LLTrace::BlockTimerStatHandle FTM_SWAP("Swap");
void swap()
{
FSTelemetry::RecordSceneTime T (FSTelemetry::SceneStatType::RENDER_SWAP);
LL_RECORD_BLOCK_TIME(FTM_SWAP);
if (gDisplaySwapBuffers)

View File

@ -3870,6 +3870,7 @@ bool check_avatar_render_mode(U32 mode)
// return (avatar->getVisualMuteSettings() == LLVOAvatar::AV_ALWAYS_RENDER);
case 4:
return FSAvatarRenderPersistence::instance().getAvatarRenderSettings(avatar->getID()) != LLVOAvatar::AV_RENDER_NORMALLY;
// return FSAvatarRenderPersistence::instance().getAvatarRenderSettings(avatar->getID()) == LLVOAvatar::AV_RENDER_NORMALLY;
default:
return false;
}

View File

@ -11691,8 +11691,8 @@ void LLVOAvatar::accountRenderComplexityForObject(
{
temp_item_complexity.insert(std::make_pair(attached_object->getID(), (U32)attachment_total_cost));
}
}
// </FS:Ansariel>
}
}
}
}

View File

@ -390,6 +390,8 @@ bool LLPipeline::sRenderTextures = true;
bool LLPipeline::sUseDepthTexture = false;
// [/RLVa:KB]
static U32 sShowTrueARC; // <FS:Beq/> True when we have the TrueArc overlay active.
// EventHost API LLPipeline listener.
static LLPipelineListener sPipelineListener;

View File

@ -621,6 +621,8 @@ public:
static bool sUseDepthTexture;
// [/RLVa:KB]
static U32 sShowTrueARC; // <FS:Beq/> True when we have the TrueArc overlay active.
static LLTrace::EventStatHandle<S64> sStatBatchSize;
//screen texture

View File

@ -198,7 +198,6 @@
function="Avatar.SetImpostorMode"
parameter="0" />
</menu_item_check>
<menu_item_call
enabled="false"
label="Block Particle Owner"

View File

@ -306,22 +306,22 @@ Settings groups to be restored (backup will always save all):
<!-- We must have this entry before the other windlight entries to have the
windlight/ base folder created. Since we can't hide rows make it so the
user can't deselect it at least -->
<row enabled="false" name="restore_folders_row_windlight">
<row enabled="false" name="restore_folders_row_windlight" value="windlight">
<column type="checkbox" name="restore_global_folders_check" value="true" enabled="false" />
<column name="restore_global_folders_label">Windlight (Mandatory)</column>
<column name="value">windlight</column>
</row>
<row name="restore_folders_row_daycycles">
<row name="restore_folders_row_daycycles" value="windlight_daycycles">
<column type="checkbox" name="restore_global_folders_check" value="true" />
<column name="restore_global_folders_label">Windlight Day Cycles</column>
<column name="value">windlight/days</column>
</row>
<row name="restore_folders_row_skies">
<row name="restore_folders_row_skies" value="windlight_skies">
<column type="checkbox" name="restore_global_folders_check" value="true" />
<column name="restore_global_folders_label">Windlight Skies</column>
<column name="value">windlight/skies</column>
</row>
<row name="restore_folders_row_waters">
<row name="restore_folders_row_waters" value="windlight_water">
<column type="checkbox" name="restore_global_folders_check" value="true" />
<column name="restore_global_folders_label">Windlight Waters</column>
<column name="value">windlight/water</column>

View File

@ -2731,7 +2731,7 @@ Try enclosing path to the editor with double quotes.
<string name="Command_MiniMap_Tooltip">Show nearby people (CTRL+SHIFT+M)</string>
<string name="Command_Move_Tooltip">Moving your avatar</string>
<string name="Command_People_Tooltip">Friends, groups, and nearby people (CTRL+SHIFT+A)</string>
<string name="Command_Performance_Tooltip">Improve graphics speed</string>
<string name="Command_Performance_Tooltip">Tune graphics settings</string>
<string name="Command_Picks_Tooltip">Places to show as favorites in your profile</string>
<string name="Command_Places_Tooltip">Places you've saved (ALT+H)</string>
<string name="Command_Preferences_Tooltip">Preferences (CTRL+P)</string>