summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Payne (Vir Linden) <vir@lindenlab.com>2018-04-24 16:55:55 +0100
committerBrad Payne (Vir Linden) <vir@lindenlab.com>2018-04-24 16:55:55 +0100
commitbc773adf618eb531fdccacd95f4cd51f6b87497a (patch)
treeabfc16457f59a332a3cef455ae635a10fc9b7520
parentf954abd9dfbf8f7147e1f5b352c8eadc6a074555 (diff)
MAINT-8549 - refactoring of streaming cost and related calculations
-rw-r--r--indra/llcharacter/lljoint.cpp2
-rw-r--r--indra/newview/llmeshrepository.cpp290
-rw-r--r--indra/newview/llmeshrepository.h47
-rw-r--r--indra/newview/llvovolume.cpp4
4 files changed, 255 insertions, 88 deletions
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index abc5a95c8d..9d10f53bed 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -440,7 +440,7 @@ void LLJoint::addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh
llclamp(pos[2],-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET));
if (constrained_pos != pos)
{
- LL_DEBUGS("Avatar") << "attachment pos override constrained to "
+ LL_DEBUGS("Avatar") << mesh_id << " joint " << getName() << " attachment pos override constrained to "
<< constrained_pos << " was " << pos << LL_ENDL;
}
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index b27fdefb83..9687376cff 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -4062,7 +4062,7 @@ void LLMeshRepository::uploadError(LLSD& args)
mUploadErrorQ.push(args);
}
-bool LLMeshRepository::getLODSizes(LLUUID mesh_id, std::vector<S32>& lod_byte_sizes, std::vector<F32>& lod_tri_counts)
+bool LLMeshRepository::getLODSizes(LLSD& header, std::vector<S32>& lod_byte_sizes, std::vector<F32>& lod_tri_counts)
{
lod_byte_sizes.resize(4);
lod_tri_counts.resize(4);
@@ -4070,120 +4070,111 @@ bool LLMeshRepository::getLODSizes(LLUUID mesh_id, std::vector<S32>& lod_byte_si
std::fill(lod_byte_sizes.begin(), lod_byte_sizes.end(), 0);
std::fill(lod_tri_counts.begin(), lod_tri_counts.end(), 0.f);
- if (mThread && mesh_id.notNull())
+ S32 bytes_high = header["high_lod"]["size"].asInteger();
+ S32 bytes_med = header["medium_lod"]["size"].asInteger();
+ if (bytes_med == 0)
{
- LLMutexLock lock(mThread->mHeaderMutex);
- LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
- if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
- {
- LLSD& header = iter->second;
- if (header.has("404")
- || !header.has("lowest_lod")
- || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION))
- {
- return false;
- }
-
- S32 bytes_high = header["high_lod"]["size"].asInteger();
- S32 bytes_med = header["medium_lod"]["size"].asInteger();
- if (bytes_med == 0)
- {
- bytes_med = bytes_high;
- }
- S32 bytes_low = header["low_lod"]["size"].asInteger();
- if (bytes_low == 0)
- {
- bytes_low = bytes_med;
- }
- S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
- if (bytes_lowest == 0)
- {
- bytes_lowest = bytes_low;
- }
- lod_byte_sizes[0] = bytes_high;
- lod_byte_sizes[1] = bytes_med;
- lod_byte_sizes[2] = bytes_low;
- lod_byte_sizes[3] = bytes_lowest;
-
- F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
- F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"
- F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
+ bytes_med = bytes_high;
+ }
+ S32 bytes_low = header["low_lod"]["size"].asInteger();
+ if (bytes_low == 0)
+ {
+ bytes_low = bytes_med;
+ }
+ S32 bytes_lowest = header["lowest_lod"]["size"].asInteger();
+ if (bytes_lowest == 0)
+ {
+ bytes_lowest = bytes_low;
+ }
+ lod_byte_sizes[0] = bytes_lowest;
+ lod_byte_sizes[1] = bytes_low;
+ lod_byte_sizes[2] = bytes_med;
+ lod_byte_sizes[3] = bytes_high;
- for (S32 i=0; i<4; i++)
- {
- lod_tri_counts[i] = llmax((F32) lod_byte_sizes[i]-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
- }
+ F32 METADATA_DISCOUNT = (F32) gSavedSettings.getU32("MeshMetaDataDiscount"); //discount 128 bytes to cover the cost of LLSD tags and compression domain overhead
+ F32 MINIMUM_SIZE = (F32) gSavedSettings.getU32("MeshMinimumByteSize"); //make sure nothing is "free"
+ F32 bytes_per_triangle = (F32) gSavedSettings.getU32("MeshBytesPerTriangle");
- return true;
- }
+ for (S32 i=0; i<4; i++)
+ {
+ lod_tri_counts[i] = llmax((F32) lod_byte_sizes[i]-METADATA_DISCOUNT, MINIMUM_SIZE)/bytes_per_triangle;
}
- return false;
+
+ return true;
}
F32 LLMeshRepository::getEstTrianglesMax(LLUUID mesh_id)
{
- std::vector<S32> lod_byte_sizes;
- std::vector<F32> lod_tri_counts;
- bool succ = getLODSizes(mesh_id, lod_byte_sizes, lod_tri_counts);
- if (!succ)
+ LLMeshCostData costs;
+ if (getCostData(mesh_id, costs))
+ {
+ return costs.mEstTrisMax;
+ }
+ else
{
return 0.f;
}
-
- return llmax(lod_tri_counts[0], lod_tri_counts[1], lod_tri_counts[2], lod_tri_counts[3]);
}
F32 LLMeshRepository::getEstTrianglesStreamingCost(LLUUID mesh_id)
{
- std::vector<S32> lod_byte_sizes;
- std::vector<F32> tris_by_lod;
- bool succ = getLODSizes(mesh_id, lod_byte_sizes, tris_by_lod);
-
- if (!succ)
+ LLMeshCostData costs;
+ if (getCostData(mesh_id, costs))
{
- LL_DEBUGS("StreamingCost") << "couldn't get tris_by_lod" << LL_ENDL;
- return 0.f;
+ return costs.computeEstTrisForStreamingCost();
}
-
- LL_DEBUGS("StreamingCost") << "tris_by_lod: "
- << tris_by_lod[0] << ", "
- << tris_by_lod[1] << ", "
- << tris_by_lod[2] << ", "
- << tris_by_lod[3] << LL_ENDL;
-
- F32 charged_tris = tris_by_lod[0];
- F32 allowed_tris = tris_by_lod[0];
- const F32 ENFORCE_FLOOR = 64.0f;
- for (S32 i=1; i<4; i++)
+ else
{
- // How many tris can we have in this LOD without affecting land impact?
- // - normally an LOD should be at most half the size of the previous one.
- // - once we reach a floor of ENFORCE_FLOOR, don't require LODs to get any smaller.
- allowed_tris = llclamp(allowed_tris/2.0f,ENFORCE_FLOOR,tris_by_lod[i]);
- F32 excess_tris = tris_by_lod[i]-allowed_tris;
- if (excess_tris>0.f)
- {
- LL_DEBUGS("StreamingCost") << "excess tris in lod[" << i << "] " << excess_tris << " allowed " << allowed_tris << LL_ENDL;
- charged_tris += excess_tris;
- }
+ return 0.f;
}
- return charged_tris;
}
+// FIXME replace with calc based on LLMeshCostData
F32 LLMeshRepository::getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
{
+ F32 result = 0.f;
if (mThread && mesh_id.notNull())
{
LLMutexLock lock(mThread->mHeaderMutex);
LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
{
- return getStreamingCost(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
+ result = getStreamingCost(iter->second, radius, bytes, bytes_visible, lod, unscaled_value);
}
}
- return 0.f;
+ if (result > 0.f)
+ {
+ LLMeshCostData data;
+ if (getCostData(mesh_id, data))
+ {
+ F32 ref_streaming_cost = data.computeRadiusBasedStreamingCost(radius);
+ F32 ref_weighted_tris = data.computeRadiusWeightedTris(radius);
+ if (!is_approx_equal(ref_streaming_cost,result))
+ {
+ LL_WARNS() << mesh_id << "streaming mismatch " << result << " " << ref_streaming_cost << LL_ENDL;
+ }
+ if (unscaled_value && !is_approx_equal(ref_weighted_tris,*unscaled_value))
+ {
+ LL_WARNS() << mesh_id << "weighted_tris mismatch " << *unscaled_value << " " << ref_weighted_tris << LL_ENDL;
+ }
+ if (bytes && (*bytes != data.mSizeTotal))
+ {
+ LL_WARNS() << mesh_id << "bytes mismatch " << *bytes << " " << data.mSizeTotal << LL_ENDL;
+ }
+ if (bytes_visible && (lod >=0) && (lod < 4) && (*bytes_visible != data.mSizeByLOD[lod]))
+ {
+ LL_WARNS() << mesh_id << "bytes_visible mismatch " << *bytes_visible << " " << data.mSizeByLOD[lod] << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS() << "getCostData failed!!!" << LL_ENDL;
+ }
+ }
+ return result;
}
+// FIXME replace with calc based on LLMeshCostData
//static
F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32* bytes_visible, S32 lod, F32 *unscaled_value)
{
@@ -4289,6 +4280,141 @@ F32 LLMeshRepository::getStreamingCost(LLSD& header, F32 radius, S32* bytes, S32
return weighted_avg/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
}
+LLMeshCostData::LLMeshCostData()
+{
+ mSizeByLOD.resize(4);
+ mEstTrisByLOD.resize(4);
+
+ std::fill(mSizeByLOD.begin(), mSizeByLOD.end(), 0);
+ std::fill(mEstTrisByLOD.begin(), mEstTrisByLOD.end(), 0.f);
+
+ mSizeTotal = 0;
+ mEstTrisMax = 0;
+}
+
+F32 LLMeshCostData::computeRadiusWeightedTris(F32 radius)
+{
+ F32 max_distance = 512.f;
+
+ F32 dlowest = llmin(radius/0.03f, max_distance);
+ F32 dlow = llmin(radius/0.06f, max_distance);
+ F32 dmid = llmin(radius/0.24f, max_distance);
+
+ F32 triangles_lowest = mEstTrisByLOD[0];
+ F32 triangles_low = mEstTrisByLOD[1];
+ F32 triangles_mid = mEstTrisByLOD[2];
+ F32 triangles_high = mEstTrisByLOD[3];
+
+ F32 max_area = 102944.f; //area of circle that encompasses region (see MAINT-6559)
+ F32 min_area = 1.f;
+
+ F32 high_area = llmin(F_PI*dmid*dmid, max_area);
+ F32 mid_area = llmin(F_PI*dlow*dlow, max_area);
+ F32 low_area = llmin(F_PI*dlowest*dlowest, max_area);
+ F32 lowest_area = max_area;
+
+ lowest_area -= low_area;
+ low_area -= mid_area;
+ mid_area -= high_area;
+
+ high_area = llclamp(high_area, min_area, max_area);
+ mid_area = llclamp(mid_area, min_area, max_area);
+ low_area = llclamp(low_area, min_area, max_area);
+ lowest_area = llclamp(lowest_area, min_area, max_area);
+
+ F32 total_area = high_area + mid_area + low_area + lowest_area;
+ high_area /= total_area;
+ mid_area /= total_area;
+ low_area /= total_area;
+ lowest_area /= total_area;
+
+ F32 weighted_avg = triangles_high*high_area +
+ triangles_mid*mid_area +
+ triangles_low*low_area +
+ triangles_lowest*lowest_area;
+
+ return weighted_avg;
+}
+
+F32 LLMeshCostData::computeEstTrisForStreamingCost()
+{
+ LL_DEBUGS("StreamingCost") << "tris_by_lod: "
+ << mEstTrisByLOD[0] << ", "
+ << mEstTrisByLOD[1] << ", "
+ << mEstTrisByLOD[2] << ", "
+ << mEstTrisByLOD[3] << LL_ENDL;
+
+ F32 charged_tris = mEstTrisByLOD[3];
+ F32 allowed_tris = mEstTrisByLOD[3];
+ const F32 ENFORCE_FLOOR = 64.0f;
+ for (S32 i=2; i>=0; i--)
+ {
+ // How many tris can we have in this LOD without affecting land impact?
+ // - normally an LOD should be at most half the size of the previous one.
+ // - once we reach a floor of ENFORCE_FLOOR, don't require LODs to get any smaller.
+ allowed_tris = llclamp(allowed_tris/2.0f,ENFORCE_FLOOR,mEstTrisByLOD[i]);
+ F32 excess_tris = mEstTrisByLOD[i]-allowed_tris;
+ if (excess_tris>0.f)
+ {
+ LL_DEBUGS("StreamingCost") << "excess tris in lod[" << i << "] " << excess_tris << " allowed " << allowed_tris << LL_ENDL;
+ charged_tris += excess_tris;
+ }
+ }
+ return charged_tris;
+}
+
+F32 LLMeshCostData::computeRadiusBasedStreamingCost(F32 radius)
+{
+ return computeRadiusWeightedTris(radius)/gSavedSettings.getU32("MeshTriangleBudget")*15000.f;
+}
+
+F32 LLMeshCostData::computeTriangleBasedStreamingCost()
+{
+ F32 result = ANIMATED_OBJECT_COST_PER_KTRI * 0.001 * computeEstTrisForStreamingCost()/0.06;
+ return result;
+}
+
+bool LLMeshRepository::getCostData(LLUUID mesh_id, LLMeshCostData& data)
+{
+ data = LLMeshCostData();
+
+ if (mThread && mesh_id.notNull())
+ {
+ LLMutexLock lock(mThread->mHeaderMutex);
+ LLMeshRepoThread::mesh_header_map::iterator iter = mThread->mMeshHeader.find(mesh_id);
+ if (iter != mThread->mMeshHeader.end() && mThread->mMeshHeaderSize[mesh_id] > 0)
+ {
+ LLSD& header = iter->second;
+
+ bool header_invalid = (header.has("404")
+ || !header.has("lowest_lod")
+ || (header.has("version") && header["version"].asInteger() > MAX_MESH_VERSION));
+ if (!header_invalid)
+ {
+ return getCostData(header, mesh_id, data);
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMeshRepository::getCostData(LLSD& header, LLUUID mesh_id, LLMeshCostData& data)
+{
+ data = LLMeshCostData();
+
+ if (!getLODSizes(header, data.mSizeByLOD, data.mEstTrisByLOD))
+ {
+ return false;
+ }
+
+ data.mEstTrisMax = llmax(data.mEstTrisByLOD[0], data.mEstTrisByLOD[1], data.mEstTrisByLOD[2], data.mEstTrisByLOD[3]);
+
+ data.mSizeTotal = data.mSizeByLOD[0] + data.mSizeByLOD[1] + data.mSizeByLOD[2] + data.mSizeByLOD[3];
+
+ return true;
+}
LLPhysicsDecomp::LLPhysicsDecomp()
: LLThread("Physics Decomp")
diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h
index bae3f66a72..e43f719471 100644
--- a/indra/newview/llmeshrepository.h
+++ b/indra/newview/llmeshrepository.h
@@ -451,6 +451,45 @@ private:
LLCore::HttpRequest::priority_t mHttpPriority;
};
+// Params related to streaming cost, render cost, and scene complexity tracking.
+struct LLMeshCostData
+{
+ LLMeshCostData();
+
+ // From the "size" field of the mesh header. LOD 0=lowest, 3=highest.
+ std::vector<S32> mSizeByLOD;
+
+ // Estimated triangle counts derived from the LOD sizes. LOD 0=lowest, 3=highest.
+ std::vector<F32> mEstTrisByLOD;
+
+ // Estimated triangle counts for the largest LOD. Typically this
+ // is also the "high" LOD, but not necessarily.
+ F32 mEstTrisMax;
+
+ // Sum of all LOD sizes.
+ S32 mSizeTotal;
+
+ // Helper functions for building data
+
+ // Triangle count as computed by original streaming cost
+ // formula. Triangles in each LOD are weighted based on how
+ // frequently they will be seen.
+ // This was called "unscaled_value" in the original getStreamingCost() functions.
+ F32 computeRadiusWeightedTris(F32 radius);
+
+ // Triangle count used by triangle-based cost formula. Based on
+ // triangles in highest LOD plus potentially partial charges for
+ // lower LODs depending on complexity.
+ F32 computeEstTrisForStreamingCost();
+
+ // Streaming cost. This should match the server-side calculation
+ // for the corresponding volume.
+ F32 computeRadiusBasedStreamingCost(F32 radius);
+
+ // New streaming cost formula, currently only used for animated objects.
+ F32 computeTriangleBasedStreamingCost();
+};
+
class LLMeshRepository
{
public:
@@ -472,12 +511,14 @@ public:
static LLDeadmanTimer sQuiescentTimer; // Time-to-complete-mesh-downloads after significant events
- bool getLODSizes(LLUUID mesh_id, std::vector<S32>& lod_byte_sizes, std::vector<F32>& lod_tri_counts);
+ bool getLODSizes(LLSD& header, std::vector<S32>& lod_byte_sizes, std::vector<F32>& lod_tri_counts);
// Estimated triangle count of the largest LOD
F32 getEstTrianglesMax(LLUUID mesh_id);
F32 getEstTrianglesStreamingCost(LLUUID mesh_id);
F32 getStreamingCost(LLUUID mesh_id, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);
+ bool getCostData(LLUUID mesh_id, LLMeshCostData& data);
+ bool getCostData(LLSD& header, LLUUID mesh_id, LLMeshCostData& data);
LLMeshRepository();
@@ -588,5 +629,9 @@ public:
extern LLMeshRepository gMeshRepo;
+// AXON make sure this is consistent with the final simulator-side values.
+const F32 ANIMATED_OBJECT_BASE_COST = 15.0f;
+const F32 ANIMATED_OBJECT_COST_PER_KTRI = 1.5f;
+
#endif
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index bf646eb44b..9db4b3de0c 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -3918,10 +3918,6 @@ F32 LLVOVolume::getStreamingCost(S32* bytes, S32* visible_bytes, F32* unscaled_v
{
F32 radius = getScale().length()*0.5f;
- // AXON make sure this is consistent with the final simulator-side values.
- const F32 ANIMATED_OBJECT_BASE_COST = 15.0f;
- const F32 ANIMATED_OBJECT_COST_PER_KTRI = 1.5f;
-
F32 linkset_base_cost = 0.f;
if (isAnimatedObject() && isRootEdit())
{