/**
* @file llfloaterlagmeter.cpp
* @brief The "Lag-o-Meter" floater used to tell users what is causing lag.
*
* $LicenseInfo:firstyear=2007&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 "llfloaterlagmeter.h"
#include "lluictrlfactory.h"
#include "llviewerstats.h"
#include "llviewertexture.h"
#include "llviewercontrol.h"
#include "llappviewer.h"
#include "lltexturefetch.h"
#include "llbutton.h"
#include "llfocusmgr.h"
#include "lltextbox.h"
#include "lfsimfeaturehandler.h"
// Nicer icons
//const std::string LAG_CRITICAL_IMAGE_NAME = "lag_status_critical.tga";
//const std::string LAG_WARNING_IMAGE_NAME = "lag_status_warning.tga";
//const std::string LAG_GOOD_IMAGE_NAME = "lag_status_good.tga";
const std::string LAG_CRITICAL_IMAGE_NAME = "LagStatus_Critical";
const std::string LAG_WARNING_IMAGE_NAME = "LagStatus_Warning";
const std::string LAG_GOOD_IMAGE_NAME = "LagStatus_Good";
//
LLFloaterLagMeter::LLFloaterLagMeter(const LLSD& key)
: LLFloater(key)
{
mCommitCallbackRegistrar.add("LagMeter.ClickShrink", boost::bind(&LLFloaterLagMeter::onClickShrink, this));
}
bool LLFloaterLagMeter::postBuild()
{
// Don't let this window take keyboard focus -- it's confusing to
// lose arrow-key driving when testing lag.
setIsChrome(true);
// were we shrunk last time?
if (isShrunk())
{
onClickShrink();
}
mClientButton = getChild("client_lagmeter");
mClientText = getChild("client_text");
mClientCause = getChild("client_lag_cause");
mNetworkButton = getChild("network_lagmeter");
mNetworkText = getChild("network_text");
mNetworkCause = getChild("network_lag_cause");
mServerButton = getChild("server_lagmeter");
mServerText = getChild("server_text");
mServerCause = getChild("server_lag_cause");
std::string config_string = getString("client_frame_rate_critical_fps", mStringArgs);
mClientFrameTimeCritical = F32Seconds(1.0f / (float)atof( config_string.c_str() ));
config_string = getString("client_frame_rate_warning_fps", mStringArgs);
mClientFrameTimeWarning = F32Seconds(1.0f / (float)atof( config_string.c_str() ));
config_string = getString("network_packet_loss_critical_pct", mStringArgs);
mNetworkPacketLossCritical = F32Percent((float)atof( config_string.c_str() ));
config_string = getString("network_packet_loss_warning_pct", mStringArgs);
mNetworkPacketLossWarning = F32Percent((float)atof( config_string.c_str() ));
config_string = getString("network_ping_critical_ms", mStringArgs);
mNetworkPingCritical = F32Milliseconds((float)atof( config_string.c_str() ));
config_string = getString("network_ping_warning_ms", mStringArgs);
mNetworkPingWarning = F32Milliseconds((float)atof( config_string.c_str() ));
config_string = getString("server_frame_rate_critical_fps", mStringArgs);
mServerFrameTimeCritical = F32Seconds(1.0f / (float)atof( config_string.c_str() ));
config_string = getString("server_frame_rate_warning_fps", mStringArgs);
mServerFrameTimeWarning = F32Seconds(1.0f / (float)atof( config_string.c_str() ));
config_string = getString("server_single_process_max_time_ms", mStringArgs);
mServerSingleProcessMaxTime = F32Seconds((float)atof( config_string.c_str() ));
// mShrunk = false;
config_string = getString("max_width_px", mStringArgs);
mMaxWidth = atoi( config_string.c_str() );
config_string = getString("min_width_px", mStringArgs);
mMinWidth = atoi( config_string.c_str() );
mStringArgs["[CLIENT_FRAME_RATE_CRITICAL]"] = getString("client_frame_rate_critical_fps");
mStringArgs["[CLIENT_FRAME_RATE_WARNING]"] = getString("client_frame_rate_warning_fps");
mStringArgs["[NETWORK_PACKET_LOSS_CRITICAL]"] = getString("network_packet_loss_critical_pct");
mStringArgs["[NETWORK_PACKET_LOSS_WARNING]"] = getString("network_packet_loss_warning_pct");
mStringArgs["[NETWORK_PING_CRITICAL]"] = getString("network_ping_critical_ms");
mStringArgs["[NETWORK_PING_WARNING]"] = getString("network_ping_warning_ms");
mStringArgs["[SERVER_FRAME_RATE_CRITICAL]"] = getString("server_frame_rate_critical_fps");
mStringArgs["[SERVER_FRAME_RATE_WARNING]"] = getString("server_frame_rate_warning_fps");
// childSetAction("minimize", onClickShrink, this);
updateControls(isShrunk()); // if expanded append colon to the labels (EXT-4079)
return true;
}
LLFloaterLagMeter::~LLFloaterLagMeter()
{
// save shrunk status for next time
// gSavedSettings.setBOOL("LagMeterShrunk", mShrunk);
// expand so we save the large window rectangle
if (isShrunk())
{
onClickShrink();
}
}
void LLFloaterLagMeter::draw()
{
determineClient();
determineNetwork();
determineServer();
LLFloater::draw();
}
void LLFloaterLagMeter::determineClient()
{
F32Milliseconds client_frame_time = LLTrace::get_frame_recording().getPeriodMean(LLStatViewer::FRAME_STACKTIME);
bool find_cause = false;
if (!gFocusMgr.getAppHasFocus())
{
mClientButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME));
mClientButton->setImagePressed(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mClientText->setText( getString("client_frame_time_window_bg_msg", mStringArgs) );
mClientCause->setText( LLStringUtil::null );
}
else if(client_frame_time >= mClientFrameTimeCritical)
{
mClientButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
mClientButton->setImagePressed(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mClientText->setText( getString("client_frame_time_critical_msg", mStringArgs) );
find_cause = true;
}
else if(client_frame_time >= mClientFrameTimeWarning)
{
mClientButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME));
mClientButton->setImagePressed(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mClientText->setText( getString("client_frame_time_warning_msg", mStringArgs) );
find_cause = true;
}
else
{
mClientButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME));
mClientButton->setImagePressed(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mClientText->setText( getString("client_frame_time_normal_msg", mStringArgs) );
mClientCause->setText( LLStringUtil::null );
}
if(find_cause)
{
if(gSavedSettings.getF32("RenderFarClip") > 128.f)
{
mClientCause->setText( getString("client_draw_distance_cause_msg", mStringArgs) );
}
else if(LLAppViewer::instance()->getTextureFetch()->getNumRequests() > 2)
{
mClientCause->setText( getString("client_texture_loading_cause_msg", mStringArgs) );
}
else
{
mClientCause->setText( getString("client_complex_objects_cause_msg", mStringArgs) );
}
}
}
void LLFloaterLagMeter::determineNetwork()
{
LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
F32Percent packet_loss = frame_recording.getPeriodMean(LLStatViewer::PACKETS_LOST_PERCENT);
F32Milliseconds ping_time = frame_recording.getPeriodMean(LLStatViewer::SIM_PING);
bool find_cause_loss = false;
bool find_cause_ping = false;
// *FIXME: We can't blame a large ping time on anything in
// particular if the frame rate is low, because a low frame
// rate is a sure recipe for bad ping times right now until
// the network handlers are de-synched from the rendering.
F32Milliseconds client_frame_time = frame_recording.getPeriodMean(LLStatViewer::FRAME_STACKTIME);
// Todo: account for LLPacketRing dropped packets? viewer drops those when it can't keep up
if(packet_loss >= mNetworkPacketLossCritical)
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
mNetworkButton->setImagePressed(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mNetworkText->setText( getString("network_packet_loss_critical_msg", mStringArgs) );
find_cause_loss = true;
}
else if(ping_time >= mNetworkPingCritical)
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
mNetworkButton->setImagePressed(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
if (client_frame_time < mNetworkPingCritical)
{
mNetworkText->setText( getString("network_ping_critical_msg", mStringArgs) );
find_cause_ping = true;
}
}
else if(packet_loss >= mNetworkPacketLossWarning)
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME));
mNetworkButton->setImagePressed(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mNetworkText->setText( getString("network_packet_loss_warning_msg", mStringArgs) );
find_cause_loss = true;
}
else if(ping_time >= mNetworkPingWarning)
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME));
mNetworkButton->setImagePressed(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
if (client_frame_time < mNetworkPingWarning)
{
mNetworkText->setText( getString("network_ping_warning_msg", mStringArgs) );
find_cause_ping = true;
}
}
else
{
mNetworkButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME));
mNetworkButton->setImagePressed(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mNetworkText->setText( getString("network_performance_normal_msg", mStringArgs) );
}
if(find_cause_loss)
{
mNetworkCause->setText( getString("network_packet_loss_cause_msg", mStringArgs) );
}
else if(find_cause_ping)
{
mNetworkCause->setText( getString("network_ping_cause_msg", mStringArgs) );
}
else
{
mNetworkCause->setText( LLStringUtil::null );
}
}
void LLFloaterLagMeter::determineServer()
{
// FIRE-16857: Lag-meter special adjustments for OpenSim
mServerFrameTimeWarning = F32Seconds(1.0f / LFSimFeatureHandler::instance().simulatorFPSWarn());
mServerFrameTimeCritical = F32Seconds(1.0f / LFSimFeatureHandler::instance().simulatorFPSCrit());
//
F32Milliseconds sim_frame_time = LLTrace::get_frame_recording().getLastRecording().getLastValue(LLStatViewer::SIM_FRAME_TIME);
bool find_cause = false;
if(sim_frame_time >= mServerFrameTimeCritical)
{
mServerButton->setImageUnselected(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME));
mServerButton->setImagePressed(LLUI::getUIImage(LAG_CRITICAL_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mServerText->setText( getString("server_frame_time_critical_msg", mStringArgs) );
find_cause = true;
}
else if(sim_frame_time >= mServerFrameTimeWarning)
{
mServerButton->setImageUnselected(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME));
mServerButton->setImagePressed(LLUI::getUIImage(LAG_WARNING_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mServerText->setText( getString("server_frame_time_warning_msg", mStringArgs) );
find_cause = true;
}
else
{
mServerButton->setImageUnselected(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME));
mServerButton->setImagePressed(LLUI::getUIImage(LAG_GOOD_IMAGE_NAME)); // FIRE-20484: Wrong texture shown on click
mServerText->setText( getString("server_frame_time_normal_msg", mStringArgs) );
mServerCause->setText( LLStringUtil::null );
}
if(find_cause)
{
LLTrace::Recording& last_recording = LLTrace::get_frame_recording().getLastRecording();
if(last_recording.getLastValue(LLStatViewer::SIM_PHYSICS_TIME) > mServerSingleProcessMaxTime)
{
mServerCause->setText( getString("server_physics_cause_msg", mStringArgs) );
}
else if(last_recording.getLastValue(LLStatViewer::SIM_SCRIPTS_TIME) > mServerSingleProcessMaxTime)
{
mServerCause->setText( getString("server_scripts_cause_msg", mStringArgs) );
}
else if(last_recording.getLastValue(LLStatViewer::SIM_NET_TIME) > mServerSingleProcessMaxTime)
{
mServerCause->setText( getString("server_net_cause_msg", mStringArgs) );
}
else if(last_recording.getLastValue(LLStatViewer::SIM_AGENTS_TIME) > mServerSingleProcessMaxTime)
{
mServerCause->setText( getString("server_agent_cause_msg", mStringArgs) );
}
else if(last_recording.getLastValue(LLStatViewer::SIM_IMAGES_TIME) > mServerSingleProcessMaxTime)
{
mServerCause->setText( getString("server_images_cause_msg", mStringArgs) );
}
else
{
mServerCause->setText( getString("server_generic_cause_msg", mStringArgs) );
}
}
}
void LLFloaterLagMeter::updateControls(bool shrink)
{
// LLFloaterLagMeter * self = (LLFloaterLagMeter*)data;
LLButton * button = getChild("minimize");
S32 delta_width = mMaxWidth -mMinWidth;
LLRect r = getRect();
if(!shrink)
{
setTitle(getString("max_title_msg", mStringArgs) );
// make left edge appear to expand
r.translate(-delta_width, 0);
setRect(r);
reshape(mMaxWidth, getRect().getHeight());
getChild("client")->setValue(getString("client_text_msg", mStringArgs) + ":");
getChild("network")->setValue(getString("network_text_msg",mStringArgs) + ":");
getChild("server")->setValue(getString("server_text_msg", mStringArgs) + ":");
// usually "<<"
button->setLabel( getString("smaller_label", mStringArgs) );
}
else
{
setTitle( getString("min_title_msg", mStringArgs) );
// make left edge appear to collapse
r.translate(delta_width, 0);
setRect(r);
reshape(mMinWidth, getRect().getHeight());
getChild("client")->setValue(getString("client_text_msg", mStringArgs) );
getChild("network")->setValue(getString("network_text_msg",mStringArgs) );
getChild("server")->setValue(getString("server_text_msg", mStringArgs) );
// usually ">>"
button->setLabel( getString("bigger_label", mStringArgs) );
}
// Don't put keyboard focus on the button
button->setFocus(false);
// self->mClientText->setVisible(self->mShrunk);
// self->mClientCause->setVisible(self->mShrunk);
// self->getChildView("client_help")->setVisible( self->mShrunk);
// self->mNetworkText->setVisible(self->mShrunk);
// self->mNetworkCause->setVisible(self->mShrunk);
// self->getChildView("network_help")->setVisible( self->mShrunk);
// self->mServerText->setVisible(self->mShrunk);
// self->mServerCause->setVisible(self->mShrunk);
// self->getChildView("server_help")->setVisible( self->mShrunk);
// self->mShrunk = !self->mShrunk;
}
bool LLFloaterLagMeter::isShrunk()
{
return gSavedSettings.getBOOL("LagMeterShrunk");
}
void LLFloaterLagMeter::onClickShrink() // toggle "LagMeterShrunk"
{
bool shrunk = isShrunk();
updateControls(!shrunk);
gSavedSettings.setBOOL("LagMeterShrunk", !shrunk);
}