MAINT-8549 - refactoring of streaming cost and related calculations

meow-7.2.2
Brad Payne (Vir Linden) 2018-04-24 16:55:55 +01:00
parent f954abd9df
commit bc773adf61
4 changed files with 258 additions and 91 deletions

View File

@ -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;
}

View File

@ -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")

View File

@ -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

View File

@ -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())
{