From 1f58884db9c026c1a74f693fc84e37a2f66830e3 Mon Sep 17 00:00:00 2001
From: RunitaiLinden <davep@lindenlab.com>
Date: Wed, 28 Aug 2024 13:14:58 -0500
Subject: #2438 Address frame stalls in updateImageDecodePriority

---
 indra/newview/llface.cpp              |   4 +-
 indra/newview/llface.h                |   6 +-
 indra/newview/llviewertexturelist.cpp | 129 ++++++++++++++++++----------------
 indra/newview/llvovolume.cpp          |   1 +
 4 files changed, 78 insertions(+), 62 deletions(-)

(limited to 'indra')

diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index a8001699fe..df08dcf503 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -2143,7 +2143,7 @@ F32 LLFace::getTextureVirtualSize()
         face_area =  mPixelArea / llclamp(texel_area, 0.015625f, 128.f);
     }
 
-    face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area) ;
+    face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area);
     if(face_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping.
     {
         if(mImportanceToCamera > LEAST_IMPORTANCE_FOR_LARGE_IMAGE && mTexture[LLRender::DIFFUSE_MAP].notNull() && mTexture[LLRender::DIFFUSE_MAP]->isLargeImage())
@@ -2161,7 +2161,6 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE;
 
-    //VECTORIZE THIS
     //get area of circle around face
 
     LLVector4a center;
@@ -2286,6 +2285,7 @@ const F32 FACE_IMPORTANCE_TO_CAMERA_OVER_ANGLE[FACE_IMPORTANCE_LEVEL][2] =    //
 //static
 F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
 {
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE;
     F32 importance = 0.f ;
 
     if(cos_angle_to_view_dir > LLViewerCamera::getInstance()->getCosHalfFov() &&
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 7cf256f731..99642016f7 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -209,7 +209,6 @@ public:
     void        setDrawInfo(LLDrawInfo* draw_info);
 
     F32         getTextureVirtualSize() ;
-    F32         getImportanceToCamera()const {return mImportanceToCamera ;}
     void        resetVirtualSize();
 
     void        setHasMedia(bool has_media)  { mHasMedia = has_media ;}
@@ -265,6 +264,11 @@ public:
     // return mSkinInfo->mHash or 0 if mSkinInfo is null
     U64 getSkinHash();
 
+    // true if face was recently in the main camera frustum according to LLViewerTextureList updates
+    bool mInFrustum = false;
+    // value of gFrameCount the last time the face was touched by LLViewerTextureList::updateImageDecodePriority
+    U32 mLastTextureUpdate = 0;
+
 private:
     LLPointer<LLVertexBuffer> mVertexBuffer;
     LLPointer<LLVertexBuffer> mVertexBufferGLTF;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 53b054cb24..2df60dbf6c 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -889,79 +889,90 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
 {
     llassert(!gCubeSnapshot);
 
-    static LLCachedControl<F32> bias_distance_scale(gSavedSettings, "TextureBiasDistanceScale", 1.f);
-    static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f);
-    static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f);
+    if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH)  // don't bother checking face list for boosted textures
+    {
+        static LLCachedControl<F32> bias_distance_scale(gSavedSettings, "TextureBiasDistanceScale", 1.f);
+        static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.04f);
+        static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f);
 
+        F32 max_vsize = 0.f;
+        bool on_screen = false;
 
-    F32 max_vsize = 0.f;
-    bool on_screen = false;
+        U32 face_count = 0;
 
-    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-    for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
-    {
-        for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi)
-        {
-            LLFace* face = (*(imagep->getFaceList(i)))[fi];
+        F32 bias = (F32) llroundf(powf(4, LLViewerTexture::sDesiredDiscardBias - 1.f));
 
-            if (face && face->getViewerObject())
+        LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+        for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+        {
+            for (S32 fi = 0; fi < imagep->getNumFaces(i); ++fi)
             {
-                F32 radius;
-                F32 cos_angle_to_view_dir;
-                static LLCachedControl<F32> bias_unimportant_threshold(gSavedSettings, "TextureBiasUnimportantFactor", 0.25f);
-                F32 vsize = face->getPixelArea();
-                bool in_frustum = face->calcPixelArea(cos_angle_to_view_dir, radius);
-
-                on_screen = in_frustum;
-
-                // Scale desired texture resolution higher or lower depending on texture scale
-                //
-                // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string
-                // shows one letter at a time
-                //
-                // Maximum usage examples: huge chunk of terrain repeats texture
-                S32 te_offset = face->getTEOffset();  // offset is -1 if not inited
-                LLViewerObject* objp = face->getViewerObject();
-                const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset);
-                F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f;
-                min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max());
-                vsize /= min_scale;
-
-                // if bias is > 2, apply to on-screen textures as well
-                bool apply_bias = LLViewerTexture::sDesiredDiscardBias > 2.f;
-
-                // apply bias to off screen objects or objects that are small on screen all the time
-                if (!in_frustum || !face->getDrawable()->isVisible() || face->getImportanceToCamera() < bias_unimportant_threshold)
-                { // further reduce by discard bias when off screen or occluded
-                    apply_bias = true;
-                }
+                LLFace* face = (*(imagep->getFaceList(i)))[fi];
 
-                if (apply_bias)
+                if (face && face->getViewerObject())
                 {
-                    F32 bias = powf(4, LLViewerTexture::sDesiredDiscardBias - 1.f);
-                    bias = (F32) llround(bias);
-                    vsize /= bias;
+                    ++face_count;
+                    F32 radius;
+                    F32 cos_angle_to_view_dir;
+                    static LLCachedControl<F32> bias_unimportant_threshold(gSavedSettings, "TextureBiasUnimportantFactor", 0.25f);
+
+                    if ((gFrameCount - face->mLastTextureUpdate) > 10)
+                    { // only call calcPixelArea at most once every 10 frames for a given face
+                        // this helps eliminate redundant calls to calcPixelArea for faces that have multiple textures
+                        // assigned to them, such as is the case with GLTF materials or Blinn-Phong materials
+                        face->mInFrustum = face->calcPixelArea(cos_angle_to_view_dir, radius);
+                        face->mLastTextureUpdate = gFrameCount;
+                    }
+
+                    F32 vsize = face->getPixelArea();
+
+                    on_screen = face->mInFrustum;
+
+                    // Scale desired texture resolution higher or lower depending on texture scale
+                    //
+                    // Minimum usage examples: a 1024x1024 texture with aplhabet, runing string
+                    // shows one letter at a time
+                    //
+                    // Maximum usage examples: huge chunk of terrain repeats texture
+                    // TODO: make this work with the GLTF texture transforms
+                    S32 te_offset = face->getTEOffset();  // offset is -1 if not inited
+                    LLViewerObject* objp = face->getViewerObject();
+                    const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset);
+                    F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f;
+                    min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max());
+                    vsize /= min_scale;
+
+                    // apply bias to offscreen faces all the time, but only to onscreen faces when bias is large
+                    if (!face->mInFrustum || LLViewerTexture::sDesiredDiscardBias > 2.f)
+                    {
+                        vsize /= bias;
+                    }
+
+                    max_vsize = llmax(max_vsize, vsize);
                 }
-
-                max_vsize = llmax(max_vsize, vsize);
             }
         }
-    }
 
-    if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE)
-    { // conditionally reset max virtual size for unboosted LOD_TEXTURES
-      // this is an alternative to decaying mMaxVirtualSize over time
-      // that keeps textures from continously downrezzing and uprezzing in the background
-
-        if (LLViewerTexture::sDesiredDiscardBias > 2.f ||
-            (!on_screen && LLViewerTexture::sDesiredDiscardBias > 1.f))
-        {
-            imagep->mMaxVirtualSize = 0.f;
+        if (face_count > 1024)
+        { // this texture is used in so many places we should just boost it and not bother checking its vsize
+            // this is especially important because the above is not time sliced and can hit multiple ms for a single texture
+            imagep->setBoostLevel(LLViewerFetchedTexture::BOOST_HIGH);
         }
-    }
 
+        if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE)
+        { // conditionally reset max virtual size for unboosted LOD_TEXTURES
+          // this is an alternative to decaying mMaxVirtualSize over time
+          // that keeps textures from continously downrezzing and uprezzing in the background
+
+            if (LLViewerTexture::sDesiredDiscardBias > 2.f ||
+                (!on_screen && LLViewerTexture::sDesiredDiscardBias > 1.f))
+            {
+                imagep->mMaxVirtualSize = 0.f;
+            }
+        }
 
-    imagep->addTextureStats(max_vsize);
+        imagep->addTextureStats(max_vsize);
+    }
 
 #if 0
     imagep->setDebugText(llformat("%d/%d - %d/%d -- %d/%d",
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index dcaf85b894..b7738b9135 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -3219,6 +3219,7 @@ void LLVOVolume::updateSpotLightPriority()
     {
         return;
     }
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME;
 
     F32 r = getLightRadius();
     LLVector3 pos = mDrawable->getPositionAgent();
-- 
cgit v1.2.3