From e3fec30097a28689a7022d06005d7d59a3004f0c Mon Sep 17 00:00:00 2001
From: "Nyx (Neal Orman)" <nyx@lindenlab.com>
Date: Wed, 1 Sep 2010 10:59:45 -0400
Subject: [PATCH] CTS-231 WIP create new ARC algorithm to be more accurate and
 account for meshes

First pass - uses the new algorithm to hopefully be more accurate of render load
on low-end machines. Also accounts for mesh complexity, including if a mesh is
weighted or non-weighted.

Code reviewed by davep
---
 indra/newview/llfloatertools.cpp   |   8 +-
 indra/newview/llmeshrepository.cpp |  24 ++++
 indra/newview/llmeshrepository.h   |   3 +-
 indra/newview/llvoavatar.cpp       |  17 ++-
 indra/newview/llvovolume.cpp       | 211 ++++++++++++++++++++++++++---
 indra/newview/llvovolume.h         |   5 +-
 6 files changed, 236 insertions(+), 32 deletions(-)

diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
index 5472531fa7..fa5d9b0892 100644
--- a/indra/newview/llfloatertools.cpp
+++ b/indra/newview/llfloatertools.cpp
@@ -1007,7 +1007,7 @@ void LLFloaterTools::onClickGridOptions()
 S32 LLFloaterTools::calcRenderCost()
 {
 	S32 cost = 0;
-	std::set<LLUUID> textures;
+	LLVOVolume::texture_cost_t textures;
 
 	for (LLObjectSelection::iterator selection_iter = LLSelectMgr::getInstance()->getSelection()->begin();
 		  selection_iter != LLSelectMgr::getInstance()->getSelection()->end();
@@ -1020,7 +1020,11 @@ S32 LLFloaterTools::calcRenderCost()
 			if (viewer_volume)
 			{
 				cost += viewer_volume->getRenderCost(textures);
-				cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
+				for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+				{
+					// add the cost of each individual texture in the linkset
+					cost += iter->second;
+				}
 				textures.clear();
 			}
 		}
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index 96a170ef07..069642b711 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -2104,6 +2104,30 @@ void LLMeshRepository::uploadModel(std::vector<LLModelInstance>& data, LLVector3
 	thread->start();
 }
 
+S32 LLMeshRepository::getMeshSize(const LLUUID& mesh_id, S32 lod)
+{
+	if (mThread)
+	{
+		LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
+		if (iter != mThread->mMeshHeader.end())
+		{
+			LLSD& header = iter->second;
+
+			if (header.has("404"))
+			{
+				return -1;
+			}
+
+			S32 size = header[header_lod[lod]]["size"].asInteger();
+			return size;
+		}
+
+	}
+
+	return -1;
+
+}
+
 void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data)
 {
 	//write model file to memory buffer
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index d5e21c35cc..b26598ce31 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -405,8 +405,7 @@ public:
 
 	void uploadModel(std::vector<LLModelInstance>& data, LLVector3& scale, bool upload_textures);
 
-	
-
+	S32 getMeshSize(const LLUUID& mesh_id, S32 lod);
 
 	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;
 	mesh_load_map mLoadingMeshes[4];
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 0c8a518de0..a6b3c436f4 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -7968,7 +7968,7 @@ void LLVOAvatar::getImpostorValues(LLVector4a* extents, LLVector3& angle, F32& d
 
 void LLVOAvatar::idleUpdateRenderCost()
 {
-	static const U32 ARC_BODY_PART_COST = 20;
+	static const U32 ARC_BODY_PART_COST = 200;
 	static const U32 ARC_LIMIT = 2048;
 
 	static std::set<LLUUID> all_textures;
@@ -7979,7 +7979,7 @@ void LLVOAvatar::idleUpdateRenderCost()
 	}
 
 	U32 cost = 0;
-	std::set<LLUUID> textures;
+	LLVOVolume::texture_cost_t textures;
 
 	for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
 	{
@@ -8017,16 +8017,24 @@ void LLVOAvatar::idleUpdateRenderCost()
 				}
 			}
 		}
+
 	}
+
+	for (LLVOVolume::texture_cost_t::iterator iter = textures.begin(); iter != textures.end(); ++iter)
+	{
+		// add the cost of each individual texture in the linkset
+		cost += iter->second;
+	}
+
 	// Diagnostic output to identify all avatar-related textures.
 	// Does not affect rendering cost calculation.
 	// Could be wrapped in a debug option if output becomes problematic.
 	if (isSelf())
 	{
 		// print any attachment textures we didn't already know about.
-		for (std::set<LLUUID>::iterator it = textures.begin(); it != textures.end(); ++it)
+		for (LLVOVolume::texture_cost_t::iterator it = textures.begin(); it != textures.end(); ++it)
 		{
-			LLUUID image_id = *it;
+			LLUUID image_id = it->first;
 			if( image_id.isNull() || image_id == IMG_DEFAULT || image_id == IMG_DEFAULT_AVATAR)
 				continue;
 			if (all_textures.find(image_id) == all_textures.end())
@@ -8057,7 +8065,6 @@ void LLVOAvatar::idleUpdateRenderCost()
 			}
 		}
 	}
-	cost += textures.size() * LLVOVolume::ARC_TEXTURE_COST;
 
 	setDebugText(llformat("%d", cost));
 	F32 green = 1.f-llclamp(((F32) cost-(F32)ARC_LIMIT)/(F32)ARC_LIMIT, 0.f, 1.f);
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index 0a7611c527..bf4cfc870f 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -2872,17 +2872,31 @@ const LLMatrix4 LLVOVolume::getRenderMatrix() const
 // total cost is returned value + 5 * size of the resulting set.
 // Cannot include cost of textures, as they may be re-used in linked
 // children, and cost should only be increased for unique textures  -Nyx
-U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
+U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const
 {
 	// base cost of each prim should be 10 points
 	static const U32 ARC_PRIM_COST = 10;
+	
+	// get access to params we'll need at various points
+	LLVolumeParams volume_params = getVolume()->getParams();
+	LLPathParams path_params = volume_params.getPathParams();
+	LLProfileParams profile_params = volume_params.getProfileParams();
+
 	// per-prim costs
 	static const U32 ARC_INVISI_COST = 1;
-	static const U32 ARC_SHINY_COST = 1;
-	static const U32 ARC_GLOW_COST = 1;
-	static const U32 ARC_FLEXI_COST = 8;
 	static const U32 ARC_PARTICLE_COST = 16;
-	static const U32 ARC_BUMP_COST = 4;
+	static const U32 ARC_CUT_COST = 1;
+	static const U32 ARC_TEXTURE_COST = 5;
+
+	// per-prim multipliers
+	static const U32 ARC_HOLLOW_MULT = 2;
+	static const U32 ARC_TWIST_MULT = 2;
+	static const U32 ARC_CIRC_PROF_MULT = 2;
+	static const U32 ARC_CIRC_PATH_MULT = 2;
+	static const U32 ARC_GLOW_MULT = 2;
+	static const U32 ARC_BUMP_MULT = 2;
+	static const U32 ARC_FLEXI_MULT = 4;
+	static const U32 ARC_SHINY_MULT = 2;
 
 	// per-face costs
 	static const U32 ARC_PLANAR_COST = 1;
@@ -2901,6 +2915,61 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 	U32 scale = 0;
 	U32 bump = 0;
 	U32 planar = 0;
+	U32 cuts = 0;
+	U32 hollow = 0;
+	U32 twist = 0; 
+	U32 circular_profile = 0;
+	U32 circular_path = 0;
+
+	const LLDrawable* drawablep = mDrawable;
+
+	if (isSculpted())
+	{
+		if (isMesh())
+		{
+			// base cost is dependent on mesh complexity
+			// note that 3 is the highest LOD as of the time of this coding.
+			S32 size = gMeshRepo.getMeshSize(volume_params.getSculptID(),3);
+			if ( size > 0)
+			{
+				if (gMeshRepo.getSkinInfo(volume_params.getSculptID()))
+				{
+					// weighted attachment - 1 point for every 3 bytes
+					shame = (U32)(size / 3.f);
+				}
+				else
+				{
+					// non-weighted attachment - 1 point for every 4 bytes
+					shame = (U32)(size / 4.f);
+				}
+
+				if (shame == 0)
+				{
+					// someone made a really tiny mesh. 
+					shame = 1;
+				}
+			}
+			else
+			{
+				// something went wrong - user should know their content isn't render-free
+				return 0;
+			}
+		}
+		else
+		{
+			const LLSculptParams *sculpt_params = (LLSculptParams *) getParameterEntry(LLNetworkData::PARAMS_SCULPT);
+			LLUUID sculpt_id = sculpt_params->getSculptTexture();
+			if (textures.find(sculpt_id) == textures.end())
+			{
+				LLViewerFetchedTexture *texture = LLViewerTextureManager::getFetchedTexture(sculpt_id);
+				if (texture)
+				{
+					S32 texture_cost = ARC_TEXTURE_COST * (texture->getFullHeight() / 128 + texture->getFullWidth() / 128 + 1);
+					textures.insert(texture_cost_t::value_type(sculpt_id, texture_cost));
+				}
+			}
+		}
+	}
 
 	if (isFlexible())
 	{
@@ -2913,14 +2982,63 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 
 	const LLVector3& sc = getScale();
 	scale += (U32) sc.mV[0] + (U32) sc.mV[1] + (U32) sc.mV[2];
+	if (scale > 4)
+	{
+		// scale is a multiplier, cap it at 4.
+		scale = 4;
+	}
 
-	const LLDrawable* drawablep = mDrawable;
+	// add points for cut prims
+	if (path_params.getBegin() != 0.f || path_params.getEnd() != 1.f)
+	{
+		++cuts;
+	}
 
-	if (isSculpted())
+	if (profile_params.getBegin() != 0.f || profile_params.getEnd() != 1.f)
+	{
+		++cuts;
+	}
+
+	// double cost for hollow prims / sculpties
+	if (volume_params.getHollow() != 0.f)
 	{
-		const LLSculptParams *sculpt_params = (LLSculptParams *) getParameterEntry(LLNetworkData::PARAMS_SCULPT);
-		LLUUID sculpt_id = sculpt_params->getSculptTexture();
-		textures.insert(sculpt_id);
+		hollow = 1;
+	}
+
+	// twist - scale by twist extent / 90
+	if (volume_params.getTwistBegin() != 0.f)
+	{
+		U32 scale = abs((S32)(volume_params.getTwistBegin() / 90.f) + 1);
+		twist += scale;
+	}
+
+	// twist - scale by twist extent / 90
+	if (volume_params.getTwist() != 0.f)
+	{
+		U32 scale = abs((S32)(volume_params.getTwist() / 90.f) + 1);
+		twist += scale;
+	}
+
+	// double cost for circular profiles / sculpties
+	if (profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE ||
+		profile_params.getCurveType() == LL_PCODE_PROFILE_CIRCLE_HALF)
+	{
+		circular_profile = 1;
+	}
+
+	// double cost for circular paths / sculpties
+	if (path_params.getCurveType() == LL_PCODE_PATH_CIRCLE ||
+		path_params.getCurveType() == LL_PCODE_PATH_CIRCLE2)
+	{
+		circular_path = 1;
+	}
+
+	// treat sculpties as hollow prims with circular paths & profiles
+	if (isSculpted() && !isMesh())
+	{
+		hollow = 1;
+		circular_profile = 1;
+		circular_path = 1;
 	}
 
 	for (S32 i = 0; i < drawablep->getNumFaces(); ++i)
@@ -2931,7 +3049,11 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 
 		if (img)
 		{
-			textures.insert(img->getID());
+			if (textures.find(img->getID()) == textures.end())
+			{
+				S32 texture_cost = ARC_TEXTURE_COST * (img->getFullHeight() / 128 + img->getFullWidth() / 128 + 1);
+				textures.insert(texture_cost_t::value_type(img->getID(), texture_cost));
+			}
 		}
 
 		if (face->getPoolType() == LLDrawPool::POOL_ALPHA)
@@ -2940,21 +3062,24 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 		}
 		else if (img && img->getPrimaryFormat() == GL_ALPHA)
 		{
-			invisi = 1;
+			invisi++;
 		}
 
 		if (te)
 		{
 			if (te->getBumpmap())
 			{
+				// bump is a multiplier, don't add per-face
 				bump = 1;
 			}
 			if (te->getShiny())
 			{
+				// shiny is a multiplier, don't add per-face
 				shiny = 1;
 			}
 			if (te->getGlow() > 0.f)
 			{
+				// glow is a multiplier, don't add per-face
 				glow = 1;
 			}
 			if (face->mTextureMatrix != NULL)
@@ -2968,17 +3093,63 @@ U32 LLVOVolume::getRenderCost(std::set<LLUUID> &textures) const
 		}
 	}
 
+	// shame currently has the "base" cost of 10 for normal prims, variable for mesh
 
-	shame += invisi * ARC_INVISI_COST;
-	shame += shiny * ARC_SHINY_COST;
-	shame += glow * ARC_GLOW_COST;
-	shame += alpha * ARC_ALPHA_COST;
-	shame += flexi * ARC_FLEXI_COST;
+	// add modifier settings
+	shame += cuts * ARC_CUT_COST;
+	shame += planar * ARC_PLANAR_COST;
 	shame += animtex * ARC_ANIM_TEX_COST;
+	shame += alpha * ARC_ALPHA_COST;
+	shame += invisi * ARC_INVISI_COST;
+
+	// multiply shame by multipliers
+	if (hollow)
+	{
+		shame *= hollow * ARC_HOLLOW_MULT;
+	}
+
+	if (twist)
+	{
+		shame *= twist * ARC_TWIST_MULT;
+	}
+
+	if (circular_profile)
+	{
+		shame *= circular_profile * ARC_CIRC_PROF_MULT;
+	}
+
+	if (circular_path)
+	{
+		shame *= circular_path * ARC_CIRC_PATH_MULT;
+	}
+
+	if (glow)
+	{
+		shame *= glow * ARC_GLOW_MULT;
+	}
+
+	if (bump)
+	{
+		shame *= bump * ARC_BUMP_MULT;
+	}
+
+	if (flexi)
+	{
+		shame *= flexi * ARC_FLEXI_MULT;
+	}
+
+	if (shiny)
+	{
+		shame *= shiny * ARC_SHINY_MULT;
+	}
+
+	if (scale)
+	{
+		shame *= scale;
+	}
+
+	// add additional costs
 	shame += particles * ARC_PARTICLE_COST;
-	shame += bump * ARC_BUMP_COST;
-	shame += planar * ARC_PLANAR_COST;
-	shame += scale;
 
 	LLViewerObject::const_child_list_t& child_list = getChildren();
 	for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
index f97214295a..eeb98726c9 100644
--- a/indra/newview/llvovolume.h
+++ b/indra/newview/llvovolume.h
@@ -120,7 +120,8 @@ public:
 	const LLMatrix4&	getRelativeXform() const				{ return mRelativeXform; }
 	const LLMatrix3&	getRelativeXformInvTrans() const		{ return mRelativeXformInvTrans; }
 	/*virtual*/	const LLMatrix4	getRenderMatrix() const;
-				U32 	getRenderCost(std::set<LLUUID> &textures) const;
+				typedef std::map<LLUUID, S32> texture_cost_t;
+				U32 	getRenderCost(texture_cost_t &textures) const;
 
 	/*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, const LLVector3& end, 
 										  S32 face = -1,                        // which face to check, -1 = ALL_SIDES
@@ -330,8 +331,6 @@ public:
 	static LLPointer<LLObjectMediaDataClient> sObjectMediaClient;
 	static LLPointer<LLObjectMediaNavigateClient> sObjectMediaNavigateClient;
 
-	static const U32 ARC_TEXTURE_COST = 5;
-
 protected:
 	static S32 sNumLODChanges;
 	
-- 
GitLab