diff options
| author | simon <none@none> | 2013-03-28 16:06:07 -0700 | 
|---|---|---|
| committer | simon <none@none> | 2013-03-28 16:06:07 -0700 | 
| commit | d8dc74b83349752768d99b77c92a284955ee04e4 (patch) | |
| tree | 9ffa74c4cf38f8bf1b68cf361ca88ee01555978f | |
| parent | 6bb7fd40d0fdc7902f1ac9db06a479121c177d0d (diff) | |
MAINT-2426 : Viewer support for new simulator AvatarRenderInfo capability.  Reviewed by Kelly
| -rwxr-xr-x | indra/newview/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 4 | ||||
| -rw-r--r-- | indra/newview/llavatarrenderinfoaccountant.cpp | 360 | ||||
| -rw-r--r-- | indra/newview/llavatarrenderinfoaccountant.h | 54 | ||||
| -rw-r--r-- | indra/newview/llspatialpartition.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewerregion.cpp | 5 | ||||
| -rw-r--r-- | indra/newview/llviewerregion.h | 4 | ||||
| -rwxr-xr-x | indra/newview/llvoavatar.cpp | 3 | ||||
| -rw-r--r-- | indra/newview/llvoavatar.h | 23 | ||||
| -rw-r--r-- | indra/newview/llvovolume.cpp | 2 | 
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; | 
