phoenix-firestorm/indra/newview/llavatarrenderinfoaccountan...

441 lines
14 KiB
C++

/**
* @file llavatarrenderinfoaccountant.cpp
* @author Dave Simmons
* @date 2013-02-28
* @brief
*
* $LicenseInfo:firstyear=2013&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2013, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* 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$
*/
// Precompiled header
#include "llviewerprecompiledheaders.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llcharacter.h"
#include "httprequest.h"
#include "httphandler.h"
#include "httpresponse.h"
#include "llcorehttputil.h"
#include "llappcorehttp.h"
#include "llavatarrendernotifier.h"
#include "lltimer.h"
#include "llviewercontrol.h"
#include "llviewermenu.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llvoavatar.h"
#include "llworld.h"
// associated header
#include "llavatarrenderinfoaccountant.h"
static const std::string KEY_AGENTS = "agents"; // map
static const std::string KEY_WEIGHT = "weight"; // integer
static const std::string KEY_TOO_COMPLEX = "tooComplex"; // bool
static const std::string KEY_OVER_COMPLEXITY_LIMIT = "overlimit"; // integer
static const std::string KEY_REPORTING_COMPLEXITY_LIMIT = "reportinglimit"; // integer
static const std::string KEY_IDENTIFIER = "identifier";
static const std::string KEY_MESSAGE = "message";
static const std::string KEY_ERROR = "error";
static const F32 SECS_BETWEEN_REGION_SCANS = 5.f; // Scan the region list every 5 seconds
static const F32 SECS_BETWEEN_REGION_REQUEST = 15.0; // Look for new avs every 15 seconds
static const F32 SECS_BETWEEN_REGION_REPORTS = 60.0; // Update each region every 60 seconds
// The policy class for HTTP traffic; this is the right value for all capability requests.
static LLCore::HttpRequest::policy_t http_policy(LLAppCoreHttp::AP_REPORTING);
// Priority for HTTP requests. Use 0U.
static LLCore::HttpRequest::priority_t http_priority(0U);
LLAvatarRenderInfoAccountant::LLAvatarRenderInfoAccountant()
: mHttpRequest(new LLCore::HttpRequest)
, mHttpHeaders(new LLCore::HttpHeaders)
, mHttpOptions(new LLCore::HttpOptions)
{
mHttpOptions->setTransferTimeout(SECS_BETWEEN_REGION_SCANS);
mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML);
mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML);
}
LLAvatarRenderInfoAccountant::~LLAvatarRenderInfoAccountant()
{
mHttpOptions->release();
mHttpHeaders->release();
// delete mHttpRequest; ???
}
// HTTP responder class for GET request for avatar render weight information
class LLAvatarRenderInfoGetHandler : public LLCore::HttpHandler
{
private:
LOG_CLASS(LLAvatarRenderInfoGetHandler);
public:
LLAvatarRenderInfoGetHandler() : LLCore::HttpHandler()
{
}
void onCompleted(LLCore::HttpHandle handle,
LLCore::HttpResponse* response)
{
LLCore::HttpStatus status = response->getStatus();
if (status)
{
LL_DEBUGS("AvatarRenderInfo") << "response"<<LL_ENDL;
LLSD avatar_render_info;
if (LLCoreHttpUtil::responseToLLSD(response, false /* quiet logging */,
avatar_render_info))
{
if (avatar_render_info.isMap())
{
if ( avatar_render_info.has(KEY_REPORTING_COMPLEXITY_LIMIT)
&& avatar_render_info.has(KEY_OVER_COMPLEXITY_LIMIT))
{
U32 reporting = avatar_render_info[KEY_REPORTING_COMPLEXITY_LIMIT].asInteger();
U32 overlimit = avatar_render_info[KEY_OVER_COMPLEXITY_LIMIT].asInteger();
LL_DEBUGS("AvatarRenderInfo") << "complexity limit: "<<reporting<<" reporting, "<<overlimit<<" over limit"<<LL_ENDL;
LLAvatarRenderNotifier::getInstance()->updateNotificationRegion(reporting, overlimit);
}
if (avatar_render_info.has(KEY_AGENTS))
{
const LLSD & agents = avatar_render_info[KEY_AGENTS];
if (agents.isMap())
{
for (LLSD::map_const_iterator agent_iter = agents.beginMap();
agent_iter != agents.endMap();
agent_iter++
)
{
LLUUID target_agent_id = LLUUID(agent_iter->first);
LLViewerObject* avatarp = gObjectList.findObject(target_agent_id);
if (avatarp && avatarp->isAvatar())
{
const LLSD & agent_info_map = agent_iter->second;
if (agent_info_map.isMap())
{
LL_DEBUGS("AvatarRenderInfo") << " Agent " << target_agent_id
<< ": " << agent_info_map << LL_ENDL;
if (agent_info_map.has(KEY_WEIGHT))
{
((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger());
}
}
else
{
LL_WARNS("AvatarRenderInfo") << "agent entry invalid"
<< " agent " << target_agent_id
<< " map " << agent_info_map
<< LL_ENDL;
}
}
else
{
LL_DEBUGS("AvatarRenderInfo") << "Unknown agent " << target_agent_id << LL_ENDL;
}
} // for agent_iter
}
else
{
LL_WARNS("AvatarRenderInfo") << "malformed get response agents avatar_render_info is not map" << LL_ENDL;
}
} // has "agents"
else if (avatar_render_info.has(KEY_ERROR))
{
const LLSD & error = avatar_render_info[KEY_ERROR];
LL_WARNS("AvatarRenderInfo") << "Avatar render info GET error: "
<< error[KEY_IDENTIFIER]
<< ": " << error[KEY_MESSAGE]
<< LL_ENDL;
}
else
{
LL_WARNS("AvatarRenderInfo") << "no agent key in get response" << LL_ENDL;
}
}
else
{
LL_WARNS("AvatarRenderInfo") << "malformed get response is not map" << LL_ENDL;
}
}
else
{
LL_WARNS("AvatarRenderInfo") << "malformed get response parse failure" << LL_ENDL;
}
}
else
{
// Something went wrong. Translate the status to
// a meaningful message.
LL_WARNS("AvatarRenderInfo") << "GET failed Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
}
delete this; // release the handler object
}
};
// HTTP responder class for POST request for avatar render weight information
class LLAvatarRenderInfoPostHandler : public LLCore::HttpHandler
{
private:
LOG_CLASS(LLAvatarRenderInfoPostHandler);
public:
LLAvatarRenderInfoPostHandler() : LLCore::HttpHandler()
{
}
void onCompleted(LLCore::HttpHandle handle,
LLCore::HttpResponse* response)
{
LLCore::HttpStatus status = response->getStatus();
if (status)
{
LL_DEBUGS("AvatarRenderInfo") << "post succeeded" << LL_ENDL;
}
else
{
// Something went wrong. Translate the status to
// a meaningful message.
LL_WARNS("AvatarRenderInfo") << "POST failed Status: "
<< status.toTerseString()
<< ", Reason: " << status.toString()
<< LL_ENDL;
}
delete this; // release the handler object
}
};
// Send request for avatar weights in one region
// called when the mRenderInfoScanTimer expires (forced when entering a new region)
void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regionp)
{
if ( regionp->getRenderInfoReportTimer().hasExpired() ) // Time to make request
{
U32 num_avs = 0;
std::string url = regionp->getCapability("AvatarRenderInfo");
if (!url.empty())
{
// Build the render info to POST to the region
LLSD agents = LLSD::emptyMap();
std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin();
while( iter != LLCharacter::sInstances.end() )
{
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(*iter);
if (avatar &&
avatar->getRezzedStatus() >= 2 && // Mostly rezzed (maybe without baked textures downloaded)
!avatar->isDead() && // Not dead yet
avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region
{
avatar->calculateUpdateRenderComplexity(); // Make sure the numbers are up-to-date
LLSD info = LLSD::emptyMap();
U32 avatar_complexity = avatar->getVisualComplexity();
if (avatar_complexity > 0)
{
// the weight/complexity is unsigned, but LLSD only stores signed integers,
// so if it's over that (which would be ridiculously high), just store the maximum signed int value
info[KEY_WEIGHT] = (S32)(avatar_complexity < S32_MAX ? avatar_complexity : S32_MAX);
info[KEY_TOO_COMPLEX] = LLSD::Boolean(avatar->isTooComplex());
agents[avatar->getID().asString()] = info;
LL_DEBUGS("AvatarRenderInfo") << "Sending avatar render info for " << avatar->getID()
<< ": " << info << LL_ENDL;
num_avs++;
}
}
iter++;
}
if (num_avs > 0)
{
LLSD report = LLSD::emptyMap();
report[KEY_AGENTS] = agents;
LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID);
LLAvatarRenderInfoPostHandler* handler = new LLAvatarRenderInfoPostHandler;
handle = LLCoreHttpUtil::requestPostWithLLSD(mHttpRequest,
http_policy,
http_priority,
url,
report,
mHttpOptions,
mHttpHeaders,
handler);
if (LLCORE_HTTP_HANDLE_INVALID == handle)
{
LLCore::HttpStatus status(mHttpRequest->getStatus());
LL_WARNS("AvatarRenderInfo") << "HTTP POST request failed"
<< " Status: " << status.toTerseString()
<< " Reason: '" << status.toString() << "'"
<< LL_ENDL;
delete handler;
}
else
{
LL_INFOS("AvatarRenderInfo") << "Sent render costs for " << num_avs
<< " avatars to region " << regionp->getName()
<< LL_ENDL;
}
}
else
{
LL_DEBUGS("AvatarRenderInfo") << "no agent info to send" << LL_ENDL;
}
}
else
{
LL_WARNS("AvatarRenderInfo") << "AvatarRenderInfo cap is empty" << LL_ENDL;
}
// Reset this regions timer, moving to longer intervals if there are lots of avatars around
regionp->getRenderInfoReportTimer().resetWithExpiry(SECS_BETWEEN_REGION_REPORTS + (2.f * num_avs));
}
}
// static
// Send request for one region, no timer checks
void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regionp)
{
if (regionp->getRenderInfoRequestTimer().hasExpired())
{
std::string url = regionp->getCapability("AvatarRenderInfo");
if (!url.empty())
{
LLAvatarRenderInfoGetHandler* handler = new LLAvatarRenderInfoGetHandler;
// First send a request to get the latest data
LLCore::HttpHandle handle = mHttpRequest->requestGet(http_policy,
http_priority,
url,
NULL,
NULL,
handler);
if (LLCORE_HTTP_HANDLE_INVALID != handle)
{
LL_INFOS("AvatarRenderInfo") << "Requested avatar render info for region "
<< regionp->getName()
<< LL_ENDL;
}
else
{
LL_WARNS("AvatarRenderInfo") << "Failed to launch HTTP GET request. Try again."
<< LL_ENDL;
delete handler;
}
}
else
{
LL_WARNS("AvatarRenderInfo") << "no AvatarRenderInfo cap for " << regionp->getName() << LL_ENDL;
}
regionp->getRenderInfoRequestTimer().resetWithExpiry(SECS_BETWEEN_REGION_REQUEST);
}
}
// static
// Called every frame - send render weight requests to every region
void LLAvatarRenderInfoAccountant::idle()
{
mHttpRequest->update(0); // give any pending http operations a chance to call completion methods
if (mRenderInfoScanTimer.hasExpired())
{
LL_DEBUGS("AvatarRenderInfo") << "Scanning regions for render info updates"
<< LL_ENDL;
// Check all regions
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
iter != LLWorld::getInstance()->getRegionList().end();
++iter)
{
LLViewerRegion* regionp = *iter;
if ( regionp
&& regionp->isAlive()
&& regionp->capabilitiesReceived())
{
// each of these is further governed by and resets its own timer
sendRenderInfoToRegion(regionp);
getRenderInfoFromRegion(regionp);
}
}
// We scanned all the regions, reset the request timer.
mRenderInfoScanTimer.resetWithExpiry(SECS_BETWEEN_REGION_SCANS);
}
}
void LLAvatarRenderInfoAccountant::resetRenderInfoScanTimer()
{
// this will force the next frame to rescan
mRenderInfoScanTimer.reset();
}
// static
// Called via LLViewerRegion::setCapabilitiesReceived() boost signals when the capabilities
// are returned for a new LLViewerRegion, and is the earliest time to get render info
void LLAvatarRenderInfoAccountant::scanNewRegion(const LLUUID& region_id)
{
LL_INFOS("AvatarRenderInfo") << region_id << LL_ENDL;
// Reset the global timer so it will scan regions on the next call to ::idle
LLAvatarRenderInfoAccountant::getInstance()->resetRenderInfoScanTimer();
LLViewerRegion* regionp = LLWorld::instance().getRegionFromID(region_id);
if (regionp)
{ // Reset the region's timers so we will:
// * request render info from it immediately
// * report on the following scan
regionp->getRenderInfoRequestTimer().reset();
regionp->getRenderInfoReportTimer().resetWithExpiry(SECS_BETWEEN_REGION_SCANS);
}
else
{
LL_WARNS("AvatarRenderInfo") << "unable to resolve region "<<region_id<<LL_ENDL;
}
}