MAINT-8549 - refactoring of streaming cost and related calculations
parent
f954abd9df
commit
bc773adf61
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
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 true;
|
||||
}
|
||||
bytes_med = bytes_high;
|
||||
}
|
||||
return false;
|
||||
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;
|
||||
|
||||
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");
|
||||
|
||||
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 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))
|
||||
{
|
||||
return costs.computeEstTrisForStreamingCost();
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS("StreamingCost") << "couldn't get tris_by_lod" << LL_ENDL;
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
// 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 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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue