summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rwxr-xr-xindra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llappviewer.cpp4
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.cpp360
-rw-r--r--indra/newview/llavatarrenderinfoaccountant.h54
-rw-r--r--indra/newview/llspatialpartition.cpp2
-rw-r--r--indra/newview/llviewerregion.cpp5
-rw-r--r--indra/newview/llviewerregion.h4
-rwxr-xr-xindra/newview/llvoavatar.cpp3
-rw-r--r--indra/newview/llvoavatar.h23
-rw-r--r--indra/newview/llvovolume.cpp2
10 files changed, 455 insertions, 4 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index bd0169fb2f..cb96dfeb48 100755
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -113,6 +113,7 @@ set(viewer_SOURCE_FILES
llavatariconctrl.cpp
llavatarlist.cpp
llavatarlistitem.cpp
+ llavatarrenderinfoaccountant.cpp
llavatarpropertiesprocessor.cpp
llblockedlistitem.cpp
llblocklist.cpp
@@ -702,6 +703,7 @@ set(viewer_HEADER_FILES
llavatarlist.h
llavatarlistitem.h
llavatarpropertiesprocessor.h
+ llavatarrenderinfoaccountant.h
llblockedlistitem.h
llblocklist.h
llbox.h
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 4745157c8b..7404d2d228 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -97,6 +97,7 @@
#include "llupdaterservice.h"
#include "llfloatertexturefetchdebugger.h"
#include "llspellcheck.h"
+#include "llavatarrenderinfoaccountant.h"
// Linden library includes
#include "llavatarnamecache.h"
@@ -4587,6 +4588,9 @@ void LLAppViewer::idle()
gObjectList.updateApparentAngles(gAgent);
}
+ // Update AV render info
+ LLAvatarRenderInfoAccountant::idle();
+
{
LLFastTimer t(FTM_AUDIO_UPDATE);
diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp
new file mode 100644
index 0000000000..04a79f7d4c
--- /dev/null
+++ b/indra/newview/llavatarrenderinfoaccountant.cpp
@@ -0,0 +1,360 @@
+/**
+ * @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"
+// associated header
+#include "llavatarrenderinfoaccountant.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llcharacter.h"
+#include "llhttpclient.h"
+#include "lltimer.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+
+// Use this for debugging
+//#define LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+static const std::string KEY_AGENTS = "agents"; // map
+static const std::string KEY_WEIGHT = "weight"; // integer
+static const std::string KEY_GEOMETRY = "geometry"; // integer
+static const std::string KEY_SURFACE = "surface"; // float
+
+static const std::string KEY_IDENTIFIER = "identifier";
+static const std::string KEY_MESSAGE = "message";
+static const std::string KEY_ERROR = "error";
+
+
+// Send data updates about once per minute, only need per-frame resolution
+LLFrameTimer LLAvatarRenderInfoAccountant::sRenderInfoReportTimer;
+
+
+// HTTP responder class for GET request for avatar render weight information
+class LLAvatarRenderInfoGetResponder : public LLHTTPClient::Responder
+{
+public:
+ LLAvatarRenderInfoGetResponder(U64 region_handle) : mRegionHandle(region_handle)
+ {
+ }
+
+ virtual void error(U32 statusNum, const std::string& reason)
+ {
+ LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
+ if (regionp)
+ {
+ llwarns << "HTTP error result for avatar weight GET: " << statusNum
+ << ", " << reason
+ << " returned by region " << regionp->getName()
+ << llendl;
+ }
+ else
+ {
+ llwarns << "Avatar render weight GET error recieved but region not found for "
+ << mRegionHandle
+ << ", error " << statusNum
+ << ", " << reason
+ << llendl;
+ }
+
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
+ if (regionp)
+ {
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Result for avatar weights request for region " << regionp->getName() << ":" << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ if (content.isMap())
+ {
+ if (content.has(KEY_AGENTS))
+ {
+ const LLSD & agents = content[KEY_AGENTS];
+ if (agents.isMap())
+ {
+ LLSD::map_const_iterator report_iter = agents.beginMap();
+ while (report_iter != agents.endMap())
+ {
+ LLUUID target_agent_id = LLUUID(report_iter->first);
+ const LLSD & agent_info_map = report_iter->second;
+ LLViewerObject* avatarp = gObjectList.findObject(target_agent_id);
+ if (avatarp &&
+ avatarp->isAvatar() &&
+ agent_info_map.isMap())
+ { // Extract the data for this avatar
+
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << " Agent " << target_agent_id
+ << ": " << agent_info_map << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ if (agent_info_map.has(KEY_WEIGHT))
+ {
+ ((LLVOAvatar *) avatarp)->setReportedVisualComplexity(agent_info_map[KEY_WEIGHT].asInteger());
+ }
+ if (agent_info_map.has(KEY_GEOMETRY))
+ {
+ ((LLVOAvatar *) avatarp)->setReportedAttachmentGeometryBytes(agent_info_map[KEY_GEOMETRY].asInteger());
+ }
+ if (agent_info_map.has(KEY_SURFACE))
+ {
+ ((LLVOAvatar *) avatarp)->setReportedAttachmentSurfaceArea((F32) agent_info_map[KEY_SURFACE].asReal());
+ }
+ }
+ report_iter++;
+ }
+ }
+ } // has "agents"
+ else if (content.has(KEY_ERROR))
+ {
+ const LLSD & error = content[KEY_ERROR];
+ llwarns << "Avatar render info GET error: "
+ << error[KEY_IDENTIFIER]
+ << ": " << error[KEY_MESSAGE]
+ << " from region " << regionp->getName()
+ << llendl;
+ }
+ }
+ }
+ else
+ {
+ llinfos << "Avatar render weight info recieved but region not found for "
+ << mRegionHandle << llendl;
+ }
+ }
+
+private:
+ U64 mRegionHandle;
+};
+
+
+// HTTP responder class for POST request for avatar render weight information
+class LLAvatarRenderInfoPostResponder : public LLHTTPClient::Responder
+{
+public:
+ LLAvatarRenderInfoPostResponder(U64 region_handle) : mRegionHandle(region_handle)
+ {
+ }
+
+ virtual void error(U32 statusNum, const std::string& reason)
+ {
+ LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
+ if (regionp)
+ {
+ llwarns << "HTTP error result for avatar weight POST: " << statusNum
+ << ", " << reason
+ << " returned by region " << regionp->getName()
+ << llendl;
+ }
+ else
+ {
+ llwarns << "Avatar render weight POST error recieved but region not found for "
+ << mRegionHandle
+ << ", error " << statusNum
+ << ", " << reason
+ << llendl;
+ }
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ LLViewerRegion * regionp = LLWorld::getInstance()->getRegionFromHandle(mRegionHandle);
+ if (regionp)
+ {
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Result for avatar weights POST for region " << regionp->getName()
+ << ": " << content << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ if (content.isMap())
+ {
+ if (content.has(KEY_ERROR))
+ {
+ const LLSD & error = content[KEY_ERROR];
+ llwarns << "Avatar render info POST error: "
+ << error[KEY_IDENTIFIER]
+ << ": " << error[KEY_MESSAGE]
+ << " from region " << regionp->getName()
+ << llendl;
+ }
+ }
+ }
+ else
+ {
+ llinfos << "Avatar render weight POST result recieved but region not found for "
+ << mRegionHandle << llendl;
+ }
+ }
+
+private:
+ U64 mRegionHandle;
+};
+
+
+// static
+// Send request for one region, no timer checks
+void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regionp)
+{
+ std::string url = regionp->getCapability("AvatarRenderInfo");
+ if (!url.empty())
+ {
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Sending avatar render info to region "
+ << regionp->getName()
+ << " from " << url
+ << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ // Build the render info to POST to the region
+ LLSD report = LLSD::emptyMap();
+ 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 && // Fully rezzed
+ !avatar->isDead() && // Not dead yet
+ avatar->getObjectHost() == regionp->getHost()) // Ensure it's on the same region
+ {
+ avatar->calculateUpdateRenderCost(); // Make sure the numbers are up-to-date
+
+ LLSD info = LLSD::emptyMap();
+ if (avatar->getVisualComplexity() > 0)
+ {
+ info[KEY_WEIGHT] = avatar->getVisualComplexity();
+ }
+ if (avatar->getAttachmentGeometryBytes() > 0)
+ {
+ info[KEY_GEOMETRY] = (S32) avatar->getAttachmentGeometryBytes();
+ }
+ if (avatar->getAttachmentSurfaceArea() > 0.f)
+ {
+ info[KEY_SURFACE] = avatar->getAttachmentSurfaceArea();
+ }
+ if (info.size() > 0)
+ {
+ agents[avatar->getID().asString()] = info;
+ }
+
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Sending avatar render info for " << avatar->getID()
+ << ": " << info << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+ }
+ iter++;
+ }
+
+ report[KEY_AGENTS] = agents;
+ if (agents.size() > 0)
+ {
+ LLHTTPClient::post(url, report, new LLAvatarRenderInfoPostResponder(regionp->getHandle()));
+ }
+ }
+}
+
+
+
+
+// static
+// Send request for one region, no timer checks
+void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regionp)
+{
+ std::string url = regionp->getCapability("AvatarRenderInfo");
+ if (!url.empty())
+ {
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Requesting avatar render info for region "
+ << regionp->getName()
+ << " from " << url
+ << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ // First send a request to get the latest data
+ LLHTTPClient::get(url, new LLAvatarRenderInfoGetResponder(regionp->getHandle()));
+ }
+}
+
+
+// static
+// Called every frame - send render weight requests to every region
+void LLAvatarRenderInfoAccountant::idle()
+{
+ if (sRenderInfoReportTimer.hasExpired())
+ {
+ const F32 SECS_BETWEEN_REGION_SCANS = 5.f; // Scan the region list every 5 seconds
+ const F32 SECS_BETWEEN_REGION_REQUEST = 60.0; // Update each region every 60 seconds
+
+ S32 num_avs = LLCharacter::sInstances.size();
+
+ // Check all regions and see if it's time to fetch/send data
+ 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() && // Region has capability URLs available
+ regionp->getRenderInfoRequestTimer().hasExpired()) // Time to make request
+ {
+ sendRenderInfoToRegion(regionp);
+ getRenderInfoFromRegion(regionp);
+
+ // Reset this regions timer, moving to longer intervals if there are lots of avatars around
+ regionp->getRenderInfoRequestTimer().resetWithExpiry(SECS_BETWEEN_REGION_REQUEST + (2.f * num_avs));
+ }
+ }
+
+ // We scanned all the regions, reset the request timer.
+ sRenderInfoReportTimer.resetWithExpiry(SECS_BETWEEN_REGION_SCANS);
+ }
+}
+
+
+// static
+// Make sRenderInfoReportTimer expire so the next call to idle() will scan and query a new region
+// 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::expireRenderInfoReportTimer()
+{
+ #ifdef LL_AVATAR_RENDER_INFO_LOG_SPAM
+ llinfos << "Viewer has new region capabilities" << llendl;
+ #endif // LL_AVATAR_RENDER_INFO_LOG_SPAM
+
+ sRenderInfoReportTimer.resetWithExpiry(0.f);
+}
+
diff --git a/indra/newview/llavatarrenderinfoaccountant.h b/indra/newview/llavatarrenderinfoaccountant.h
new file mode 100644
index 0000000000..5b4a4d3db2
--- /dev/null
+++ b/indra/newview/llavatarrenderinfoaccountant.h
@@ -0,0 +1,54 @@
+/**
+ * @file llavatarrenderinfoaccountant.h
+ * @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$
+ */
+
+#if ! defined(LL_llavatarrenderinfoaccountant_H)
+#define LL_llavatarrenderinfoaccountant_H
+
+class LLViewerRegion;
+
+// Class to gather avatar rendering information
+// that is sent to or fetched from regions.
+class LLAvatarRenderInfoAccountant
+{
+public:
+ LLAvatarRenderInfoAccountant() {};
+ ~LLAvatarRenderInfoAccountant() {};
+
+ static void sendRenderInfoToRegion(LLViewerRegion * regionp);
+ static void getRenderInfoFromRegion(LLViewerRegion * regionp);
+
+ static void expireRenderInfoReportTimer();
+
+ static void idle();
+
+private:
+ // Send data updates about once per minute, only need per-frame resolution
+ static LLFrameTimer sRenderInfoReportTimer;
+};
+
+#endif /* ! defined(LL_llavatarrenderinfoaccountant_H) */
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
index 759e1a7b4c..1d30b19308 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -1404,7 +1404,9 @@ void LLSpatialGroup::handleDestruction(const TreeNode* node)
if (bridge->mAvatar.notNull())
{
bridge->mAvatar->mAttachmentGeometryBytes -= mGeometryBytes;
+ bridge->mAvatar->mAttachmentGeometryBytes = llmax(bridge->mAvatar->mAttachmentGeometryBytes, 0);
bridge->mAvatar->mAttachmentSurfaceArea -= mSurfaceArea;
+ bridge->mAvatar->mAttachmentSurfaceArea = llmax(bridge->mAvatar->mAttachmentSurfaceArea, 0.f);
}
}
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index e4234a538d..eb5a0b8d37 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -44,6 +44,7 @@
#include "llagent.h"
#include "llagentcamera.h"
+#include "llavatarrenderinfoaccountant.h"
#include "llcallingcard.h"
#include "llcaphttpsender.h"
#include "llcapabilitylistener.h"
@@ -333,6 +334,9 @@ LLViewerRegion::LLViewerRegion(const U64 &handle,
mImpl->mObjectPartition.push_back(new LLBridgePartition()); //PARTITION_BRIDGE
mImpl->mObjectPartition.push_back(new LLHUDParticlePartition());//PARTITION_HUD_PARTICLE
mImpl->mObjectPartition.push_back(NULL); //PARTITION_NONE
+
+ mRenderInfoRequestTimer.resetWithExpiry(0.f); // Set timer to be expired
+ setCapabilitiesReceivedCallback(boost::bind(&LLAvatarRenderInfoAccountant::expireRenderInfoReportTimer));
}
@@ -1514,6 +1518,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("AgentState");
capabilityNames.append("AttachmentResources");
capabilityNames.append("AvatarPickerSearch");
+ capabilityNames.append("AvatarRenderInfo");
capabilityNames.append("CharacterProperties");
capabilityNames.append("ChatSessionRequest");
capabilityNames.append("CopyInventoryFromNotecard");
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
index c9fffaf30e..2d3a622e42 100644
--- a/indra/newview/llviewerregion.h
+++ b/indra/newview/llviewerregion.h
@@ -365,6 +365,8 @@ public:
LLDynamicArray<U32> mMapAvatars;
LLDynamicArray<LLUUID> mMapAvatarIDs;
+ LLFrameTimer & getRenderInfoRequestTimer() { return mRenderInfoRequestTimer; };
+
private:
LLViewerRegionImpl * mImpl;
@@ -421,6 +423,8 @@ private:
BOOL mReleaseNotesRequested;
LLSD mSimulatorFeatures;
+
+ LLFrameTimer mRenderInfoRequestTimer;
};
inline BOOL LLViewerRegion::getAllowDamage() const
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 9e5d44eb1f..1fc20fd207 100755
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -662,6 +662,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mSpecialRenderMode(0),
mAttachmentGeometryBytes(0),
mAttachmentSurfaceArea(0.f),
+ mReportedVisualComplexity(-1),
+ mReportedAttachmentGeometryBytes(-1),
+ mReportedAttachmentSurfaceArea(-1.f),
mTurning(FALSE),
mPelvisToFoot(0.f),
mLastSkeletonSerialNum( 0 ),
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index 0e7bf7a6c9..495d3e9483 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -257,9 +257,20 @@ public:
void calculateUpdateRenderCost();
void updateVisualComplexity() { mVisualComplexityStale = TRUE; }
- S32 getVisualComplexity() { return mVisualComplexity; };
- S32 getUpdatePeriod() { return mUpdatePeriod; };
- const LLColor4 & getMutedAVColor() { return mMutedAVColor; };
+ S32 getVisualComplexity() { return mVisualComplexity; }; // Numbers calculated here by rendering AV
+ S32 getAttachmentGeometryBytes() { return mAttachmentGeometryBytes; }; // number of bytes in attached geometry
+ F32 getAttachmentSurfaceArea() { return mAttachmentSurfaceArea; }; // estimated surface area of attachments
+
+ S32 getReportedVisualComplexity() { return mReportedVisualComplexity; }; // Numbers as reported by the SL server
+ void setReportedVisualComplexity(S32 value) { mReportedVisualComplexity = value; };
+ S32 getReportedAttachmentGeometryBytes() { return mReportedAttachmentGeometryBytes; }; //number of bytes in attached geometry
+ void setReportedAttachmentGeometryBytes(S32 value) { mReportedAttachmentGeometryBytes = value; };
+ F32 getReportedAttachmentSurfaceArea() { return mReportedAttachmentSurfaceArea; }; //estimated surface area of attachments
+ void setReportedAttachmentSurfaceArea(F32 value) { mReportedAttachmentSurfaceArea = value; };
+
+ S32 getUpdatePeriod() { return mUpdatePeriod; };
+ const LLColor4 & getMutedAVColor() { return mMutedAVColor; };
+
void idleUpdateBelowWater();
@@ -469,9 +480,13 @@ public:
static void restoreGL();
BOOL mIsDummy; // for special views
S32 mSpecialRenderMode; // special lighting
- U32 mAttachmentGeometryBytes; //number of bytes in attached geometry
+ S32 mAttachmentGeometryBytes; //number of bytes in attached geometry
F32 mAttachmentSurfaceArea; //estimated surface area of attachments
+ S32 mReportedVisualComplexity; // Numbers as reported by the SL server
+ S32 mReportedAttachmentGeometryBytes; //number of bytes in attached geometry
+ F32 mReportedAttachmentSurfaceArea; //estimated surface area of attachments
+
private:
bool shouldAlphaMask();
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 426ad8bc21..71ee2da216 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -4242,7 +4242,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
if (pAvatarVO)
{
pAvatarVO->mAttachmentGeometryBytes -= group->mGeometryBytes;
+ pAvatarVO->mAttachmentGeometryBytes = llmax(pAvatarVO->mAttachmentGeometryBytes, 0);
pAvatarVO->mAttachmentSurfaceArea -= group->mSurfaceArea;
+ pAvatarVO->mAttachmentSurfaceArea = llmax(pAvatarVO->mAttachmentSurfaceArea, 0.f);
}
group->mGeometryBytes = 0;