diff options
author | Brad Payne (Vir Linden) <vir@lindenlab.com> | 2018-04-24 16:55:55 +0100 |
---|---|---|
committer | Brad Payne (Vir Linden) <vir@lindenlab.com> | 2018-04-24 16:55:55 +0100 |
commit | bc773adf618eb531fdccacd95f4cd51f6b87497a (patch) | |
tree | abfc16457f59a332a3cef455ae635a10fc9b7520 /indra | |
parent | f954abd9dfbf8f7147e1f5b352c8eadc6a074555 (diff) |
MAINT-8549 - refactoring of streaming cost and related calculations
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llcharacter/lljoint.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llmeshrepository.cpp | 290 | ||||
-rw-r--r-- | indra/newview/llmeshrepository.h | 47 | ||||
-rw-r--r-- | indra/newview/llvovolume.cpp | 4 |
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()) { |