From 57060fa0ebfacf3705ec2d9770e83aab3c86b894 Mon Sep 17 00:00:00 2001
From: andreykproductengine <akleshchev@productengine.com>
Date: Tue, 2 Aug 2016 21:57:04 +0300
Subject: [PATCH] MAINT-4124 Warning user about render-heavy set of HUDs

---
 indra/newview/app_settings/settings.xml       |  55 ++++++++
 indra/newview/llavatarrendernotifier.cpp      | 130 ++++++++++++++++++
 indra/newview/llavatarrendernotifier.h        |  36 +++++
 indra/newview/llvoavatar.cpp                  |  65 ++++++++-
 .../skins/default/xui/en/notifications.xml    |  14 ++
 .../newview/skins/default/xui/en/strings.xml  |   7 +
 6 files changed, 302 insertions(+), 5 deletions(-)

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ca81696a502..00c769607fc 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -10092,6 +10092,17 @@
     <key>Value</key>
     <integer>10</integer>
   </map>
+  <key>ComplexityChangesPopUpDelay</key>
+  <map>
+    <key>Comment</key>
+    <string>Delay before viewer will show avatar complexity notice again</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>300</integer>
+  </map>
   <key>RenderAvatarMaxComplexity</key>
   <map>
     <key>Comment</key>
@@ -10104,6 +10115,50 @@
     <key>Value</key>
     <integer>0</integer>
   </map>
+  <key>RenderHUDObjectsWarning</key>
+  <map>
+    <key>Comment</key>
+    <string>Viewer will warn user about HUD containing to many objects if objects count is above this value</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>1000</integer>
+  </map>
+  <key>RenderHUDTexturesWarning</key>
+  <map>
+    <key>Comment</key>
+    <string>Viewer will warn user about HUD containing to many textures if texture count is above this value</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>200</integer>
+  </map>
+  <key>RenderHUDOversizedTexturesWarning</key>
+  <map>
+    <key>Comment</key>
+    <string>How many textures with size 1024 * 1024 or bigger HUD can contain before notifying user</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>6</integer>
+  </map>
+  <key>RenderHUDTexturesVirtualMemoryWarning</key>
+  <map>
+    <key>Comment</key>
+    <string>Viewer will warn user about HUD textures using memory above this value (Virtual memory, in pixels)</string>
+    <key>Persist</key>
+    <integer>1</integer>
+    <key>Type</key>
+    <string>U32</string>
+    <key>Value</key>
+    <integer>10000000</integer>
+  </map>
   <key>RenderAutoMuteSurfaceAreaLimit</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/llavatarrendernotifier.cpp b/indra/newview/llavatarrendernotifier.cpp
index 24934fdb73c..f7a1ef16218 100644
--- a/indra/newview/llavatarrendernotifier.cpp
+++ b/indra/newview/llavatarrendernotifier.cpp
@@ -51,6 +51,11 @@ static const F32 RENDER_ALLOWED_CHANGE_PCT = 0.1;
 // wait seconds before processing over limit updates after last complexity change
 static const U32 OVER_LIMIT_UPDATE_DELAY = 70;
 
+static const U32 WARN_HUD_OBJECTS_LIMIT = 1000;
+static const U32 WARN_HUD_TEXTURES_LIMIT = 200;
+static const U32 WARN_HUD_OVERSIZED_TEXTURES_LIMIT = 6;
+static const U32 WARN_HUD_TEXTURE_MEMORY_LIMIT = 10000000; // in pixels
+
 
 LLAvatarRenderNotifier::LLAvatarRenderNotifier() :
 mAgentsCount(0),
@@ -264,3 +269,128 @@ void LLAvatarRenderNotifier::updateNotificationAgent(U32 agentComplexity)
     }
 }
 
+// LLHUDRenderNotifier
+
+LLHUDRenderNotifier::LLHUDRenderNotifier()
+{
+}
+
+LLHUDRenderNotifier::~LLHUDRenderNotifier()
+{
+}
+
+void LLHUDRenderNotifier::updateNotificationHUD(LLHUDComplexity new_complexity)
+{
+    if (!isAgentAvatarValid())
+    {
+        // data not ready.
+        return;
+    }
+
+    static const char* hud_memory = "hud_render_memory_warning";
+    static const char* hud_cost = "hud_render_cost_warning";
+    static const char* hud_heavy = "hud_render_heavy_textures_warning";
+    static const char* hud_cramped = "hud_render_cramped_warning";
+    static const char* hud_textures = "hud_render_textures_warning";
+
+    static LLCachedControl<U32> max_render_cost(gSavedSettings, "RenderAvatarMaxComplexity", 0U); // ties max HUD cost to avatar cost
+    static LLCachedControl<U32> max_objects_count(gSavedSettings, "RenderHUDObjectsWarning", WARN_HUD_OBJECTS_LIMIT);
+    static LLCachedControl<U32> max_textures_count(gSavedSettings, "RenderHUDTexturesWarning", WARN_HUD_TEXTURES_LIMIT);
+    static LLCachedControl<U32> max_oversized_count(gSavedSettings, "RenderHUDOversizedTexturesWarning", WARN_HUD_OVERSIZED_TEXTURES_LIMIT);
+    static LLCachedControl<U32> max_texture_memory(gSavedSettings, "RenderHUDTexturesVirtualMemoryWarning", WARN_HUD_TEXTURE_MEMORY_LIMIT);
+
+    if (mHUDPopUpDelayTimer.hasExpired())
+    {
+        // Show warning with highest importance (5m delay between warnings by default)
+        // TODO:
+        // Consider showing message with list of issues.
+        // For now shows one after another if update arrives and timer expired, so
+        // consider showing only one most important or consider triggering not
+        // only in case of update
+        if (mReportedHUDComplexity.texturesSizeTotal < new_complexity.texturesSizeTotal
+            && new_complexity.texturesSizeTotal > max_texture_memory)
+        {
+            displayHUDNotification(hud_memory);
+            LL_DEBUGS("HUDdetail") << "HUD memory usage over limit,"
+                                   << " was " << mReportedHUDComplexity.texturesSizeTotal
+                                   << " is " << new_complexity.texturesSizeTotal << LL_ENDL;
+            mReportedHUDComplexity.texturesSizeTotal = new_complexity.texturesSizeTotal;
+        }
+        else if ((mReportedHUDComplexity.objectsCost < new_complexity.objectsCost
+            || mReportedHUDComplexity.texturesCost < new_complexity.texturesCost)
+            && max_render_cost > 0
+            && new_complexity.objectsCost + new_complexity.texturesCost > max_render_cost)
+        {
+            LL_DEBUGS("HUDdetail") << "HUD complexity over limit,"
+                                   << " HUD textures cost: " << new_complexity.texturesCost
+                                   << " HUD objects cost: " << new_complexity.objectsCost << LL_ENDL;
+            displayHUDNotification(hud_cost);
+            mReportedHUDComplexity.objectsCost = new_complexity.objectsCost;
+            mReportedHUDComplexity.texturesCost = new_complexity.texturesCost;
+        }
+        else if (mReportedHUDComplexity.largeTexturesCount < new_complexity.largeTexturesCount
+            && new_complexity.largeTexturesCount > max_oversized_count)
+        {
+            LL_DEBUGS("HUDdetail") << "HUD contains to many large textures: "
+                                   << new_complexity.largeTexturesCount << LL_ENDL;
+            displayHUDNotification(hud_heavy);
+            mReportedHUDComplexity.largeTexturesCount = new_complexity.largeTexturesCount;
+        }
+        else if (mReportedHUDComplexity.texturesCount < new_complexity.texturesCount
+            && new_complexity.texturesCount > max_textures_count)
+        {
+            LL_DEBUGS("HUDdetail") << "HUD contains too many textures: "
+                                   << new_complexity.texturesCount << LL_ENDL;
+            displayHUDNotification(hud_cramped);
+            mReportedHUDComplexity.texturesCount = new_complexity.texturesCount;
+        }
+        else if (mReportedHUDComplexity.objectsCount < new_complexity.objectsCount
+            && new_complexity.objectsCount > max_objects_count)
+        {
+            LL_DEBUGS("HUDdetail") << "HUD contains too many objects: "
+                                   << new_complexity.objectsCount << LL_ENDL;
+            displayHUDNotification(hud_textures);
+            mReportedHUDComplexity.objectsCount = new_complexity.objectsCount;
+        }
+        else
+        {
+            // all warnings displayed, just store everything so that we will
+            // be able to reduce values and show warnings again later
+            mReportedHUDComplexity = new_complexity;
+        }
+    }
+
+    if (mLatestHUDComplexity.objectsCost != new_complexity.objectsCost
+        || mLatestHUDComplexity.objectsCount != new_complexity.objectsCount
+        || mLatestHUDComplexity.texturesCost != new_complexity.texturesCost
+        || mLatestHUDComplexity.texturesCount != new_complexity.texturesCount
+        || mLatestHUDComplexity.largeTexturesCount != new_complexity.largeTexturesCount
+        || mLatestHUDComplexity.texturesSizeTotal != new_complexity.texturesSizeTotal)
+    {
+        LL_INFOS("HUDdetail") << "HUD textures count: " << new_complexity.texturesCount
+            << " HUD textures cost: " << new_complexity.texturesCost
+            << " Large textures: " << new_complexity.largeTexturesCount
+            << " HUD objects cost: " << new_complexity.objectsCost
+            << " HUD objects count: " << new_complexity.objectsCount << LL_ENDL;
+
+        mLatestHUDComplexity = new_complexity;
+    }
+    
+}
+
+void LLHUDRenderNotifier::displayHUDNotification(const char* message)
+{
+    static LLCachedControl<U32> pop_up_delay(gSavedSettings, "ComplexityChangesPopUpDelay", 300);
+    static LLCachedControl<U32> expire_delay(gSavedSettings, "ShowMyComplexityChanges", 20);
+    LLDate expire_date(LLDate::now().secondsSinceEpoch() + expire_delay);
+
+    LLSD args;
+    args["HUD_REASON"] = LLTrans::getString(message);
+
+    LLNotifications::instance().add(LLNotification::Params()
+        .name("HUDComplexityWarning")
+        .expiry(expire_date)
+        .substitutions(args));
+    mHUDPopUpDelayTimer.resetWithExpiry(pop_up_delay);
+}
+
diff --git a/indra/newview/llavatarrendernotifier.h b/indra/newview/llavatarrendernotifier.h
index 2a2704de285..959bebef024 100644
--- a/indra/newview/llavatarrendernotifier.h
+++ b/indra/newview/llavatarrendernotifier.h
@@ -33,6 +33,25 @@
 
 class LLViewerRegion;
 
+struct LLHUDComplexity
+{
+    LLHUDComplexity()
+    {
+        objectsCost = 0;
+        objectsCount = 0;
+        texturesCost = 0;
+        texturesCount = 0;
+        largeTexturesCount = 0;
+        texturesSizeTotal = 0;
+    }
+    U32 objectsCost;
+    U32 objectsCount;
+    U32 texturesCost;
+    U32 texturesCount;
+    U32 largeTexturesCount;
+    F64 texturesSizeTotal;
+};
+
 // Class to notify user about drastic changes in agent's render weights or if other agents
 // reported that user's agent is too 'heavy' for their settings
 class LLAvatarRenderNotifier : public LLSingleton<LLAvatarRenderNotifier>
@@ -81,4 +100,21 @@ class LLAvatarRenderNotifier : public LLSingleton<LLAvatarRenderNotifier>
     S32 mLastOutfitRezStatus;
 };
 
+// Class to notify user about heavy set of HUD
+class LLHUDRenderNotifier : public LLSingleton<LLHUDRenderNotifier>
+{
+public:
+    LLHUDRenderNotifier();
+    ~LLHUDRenderNotifier();
+
+    void updateNotificationHUD(LLHUDComplexity new_complexity);
+
+private:
+    void displayHUDNotification(const char* message);
+
+    LLHUDComplexity mReportedHUDComplexity;
+    LLHUDComplexity mLatestHUDComplexity;
+    LLFrameTimer mHUDPopUpDelayTimer;
+};
+
 #endif /* ! defined(LL_llavatarrendernotifier_H) */
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index cdc7e20c2c9..aebc0665074 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -186,6 +186,7 @@ const F32 NAMETAG_VERTICAL_SCREEN_OFFSET = 25.f;
 const F32 NAMETAG_VERT_OFFSET_WEIGHT = 0.17f;
 
 const U32 LLVOAvatar::VISUAL_COMPLEXITY_UNKNOWN = 0;
+const F64 HUD_OVERSIZED_TEXTURE_DATA_SIZE = 1024 * 1024;
 
 enum ERenderName
 {
@@ -8356,6 +8357,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
 	{
 		U32 cost = VISUAL_COMPLEXITY_UNKNOWN;
 		LLVOVolume::texture_cost_t textures;
+		LLHUDComplexity hud_complexity;
 
 		for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++)
 		{
@@ -8432,6 +8434,55 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
 						}
 					}
 				}
+                if (isSelf()
+                    && attached_object
+                    && attached_object->isHUDAttachment()
+                    && attached_object->mDrawable)
+                {
+                    textures.clear();
+
+                    const LLVOVolume* volume = attached_object->mDrawable->getVOVolume();
+                    if (volume)
+                    {
+                        // get cost and individual textures
+                        hud_complexity.objectsCost += volume->getRenderCost(textures);
+                        hud_complexity.objectsCount++;
+
+                        LLViewerObject::const_child_list_t& child_list = attached_object->getChildren();
+                        for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin();
+                            iter != child_list.end(); ++iter)
+                        {
+                            LLViewerObject* childp = *iter;
+                            const LLVOVolume* chld_volume = dynamic_cast<LLVOVolume*>(childp);
+                            if (chld_volume)
+                            {
+                                // get cost and individual textures
+                                hud_complexity.objectsCost += chld_volume->getRenderCost(textures);
+                                hud_complexity.objectsCount++;
+                            }
+                        }
+
+                        hud_complexity.texturesCount += textures.size();
+
+                        for (LLVOVolume::texture_cost_t::iterator volume_texture = textures.begin();
+                            volume_texture != textures.end();
+                            ++volume_texture)
+                        {
+                            // add the cost of each individual texture (ignores duplicates)
+                            hud_complexity.texturesCost += volume_texture->second;
+                            LLViewerFetchedTexture *tex = LLViewerTextureManager::getFetchedTexture(volume_texture->first);
+                            if (tex)
+                            {
+                                F64 size = tex->getMaxVirtualSize(); // in pixels
+                                hud_complexity.texturesSizeTotal += size;
+                                if (size >= HUD_OVERSIZED_TEXTURE_DATA_SIZE)
+                                {
+                                    hud_complexity.largeTexturesCount++;
+                                }
+                            }
+                        }
+                    }
+                }
 			}
 		}
 
@@ -8493,11 +8544,15 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
 
         static LLCachedControl<U32> show_my_complexity_changes(gSavedSettings, "ShowMyComplexityChanges", 20);
 
-		if (isSelf() && show_my_complexity_changes)
-		{
-			LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity);
-		}
-	}
+        if (isSelf() && show_my_complexity_changes)
+        {
+            // Avatar complexity
+            LLAvatarRenderNotifier::getInstance()->updateNotificationAgent(mVisualComplexity);
+
+            // HUD complexity
+            LLHUDRenderNotifier::getInstance()->updateNotificationHUD(hud_complexity);
+        }
+    }
 }
 
 void LLVOAvatar::setVisualMuteSettings(VisualMuteSettings set)
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 6fdeedc8ae4..102ce0ddc06 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3319,6 +3319,20 @@ Your [https://community.secondlife.com/t5/English-Knowledge-Base/Avatar-Renderin
 Your [https://community.secondlife.com/t5/English-Knowledge-Base/Avatar-Rendering-Complexity/ta-p/2967838 avatar complexity] is [AGENT_COMPLEXITY].
   </notification>
 
+  <notification
+   icon = "notifytip.tga"
+   name = "HUDComplexityWarning"
+   type = "notifytip"
+   log_to_chat = "false">
+    <unique combine = "cancel_old">
+      <context>HUDComplexityWarning</context>
+    </unique>
+    [HUD_REASON], it is likely to negatively affect your performance.
+    <usetemplate
+     ignoretext="Warn me when my HUD complexity is too high"
+     name="notifyignore"/>
+  </notification>
+
   <notification
    icon="alertmodal.tga"
    name="FirstRun"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 27c08f560fb..22441f1278d 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -2499,6 +2499,13 @@ This feature is currently in Beta. Please add your name to this [http://goo.gl/f
   <string name="av_render_most_of">You may not be rendered by most of those around you.</string>
   <string name="av_render_anyone">You may not be rendered by anyone around you.</string>
 
+  <!-- HUD complexity rendering messages, see llavatarrendernotifier. -->
+  <string name="hud_render_memory_warning">Your HUD uses a lot of texture memory</string>
+  <string name="hud_render_cost_warning">Your HUD contains a lot of expensive objects and textures</string>
+  <string name="hud_render_heavy_textures_warning">Your HUD contains a lot of large textures</string>
+  <string name="hud_render_cramped_warning">Your HUD contains too many objects</string>
+  <string name="hud_render_textures_warning">Your HUD contains too many textures</string>
+
   <!-- AgeYearsA = singular,
        AgeYearsB = plural,
        AgeYearsC = plural for non-English languages like Russian
-- 
GitLab