From dc7b2adbebea2bd332de52d90dbbe651ee94031c Mon Sep 17 00:00:00 2001
From: Cosmic Linden <cosmic@lindenlab.com>
Date: Wed, 2 Oct 2024 15:25:11 -0700
Subject: secondlife/viewer#2472: Debug tools for labeling textures

---
 indra/llrender/llgltexture.cpp          | 62 +++++++++++++++++++++
 indra/llrender/llgltexture.h            |  3 +
 indra/newview/app_settings/settings.xml | 11 ++++
 indra/newview/llviewertexturelist.cpp   | 98 ++++++++++++++++++++++++++++++++-
 indra/newview/llviewertexturelist.h     | 11 ++++
 5 files changed, 183 insertions(+), 2 deletions(-)

diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp
index 4dcca5a726..9a2b2ef524 100644
--- a/indra/llrender/llgltexture.cpp
+++ b/indra/llrender/llgltexture.cpp
@@ -168,6 +168,68 @@ bool LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw,
     return ret ;
 }
 
+void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const
+{
+    if (!mGLTexturep)
+    {
+        error = true;
+        label.clear();
+        return;
+    }
+    LLGLuint texname = mGLTexturep->getTexName();
+    if (!texname)
+    {
+        error = true;
+        label.clear();
+        return;
+    }
+    static GLsizei max_length = 0;
+    if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); }
+    static char * clabel = new char[max_length+1];
+    GLsizei length;
+    glGetObjectLabel(GL_TEXTURE, texname, max_length+1, &length, clabel);
+    error = false;
+    label.assign(clabel, length);
+}
+
+std::string LLGLTexture::setGLObjectLabel(const std::string& prefix, bool append_texname) const
+{
+    llassert(mGLTexturep);
+    if (mGLTexturep)
+    {
+        LLGLuint texname = mGLTexturep->getTexName();
+        llassert(texname);
+        if (texname)
+        {
+            static GLsizei max_length = 0;
+            if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); }
+
+            if (append_texname)
+            {
+                std::string label_with_texname = prefix + "_" + std::to_string(texname);
+                label_with_texname.resize(std::min(size_t(max_length), label_with_texname.size()));
+                glObjectLabel(GL_TEXTURE, texname, (GLsizei)label_with_texname.size(), label_with_texname.c_str());
+                return label_with_texname;
+            }
+            else
+            {
+                if (prefix.size() <= max_length)
+                {
+                    glObjectLabel(GL_TEXTURE, texname, (GLsizei)prefix.size(), prefix.c_str());
+                    return prefix;
+                }
+                else
+                {
+                    const std::string label(prefix.c_str(), max_length);
+                    glObjectLabel(GL_TEXTURE, texname, (GLsizei)label.size(), label.c_str());
+                    return label;
+                }
+            }
+        }
+    }
+    return "";
+}
+
 void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes)
 {
     llassert(mGLTexturep.notNull()) ;
diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h
index 122d2a7f9c..22f2ed5131 100644
--- a/indra/llrender/llgltexture.h
+++ b/indra/llrender/llgltexture.h
@@ -118,6 +118,9 @@ public:
     LLGLuint   getTexName() const ;
     bool       createGLTexture() ;
 
+    void getGLObjectLabel(std::string& label, bool& error) const;
+    std::string setGLObjectLabel(const std::string& prefix, bool append_texname = false) const;
+
     // Create a GL Texture from an image raw
     // discard_level - mip level, 0 for highest resultion mip
     // imageraw - the image to copy from
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ce6d9148c6..5d3f3d58c6 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -7932,6 +7932,17 @@
       <key>Value</key>
       <integer>0</integer>
     </map>
+    <key>RenderDebugTextureLabel</key>
+    <map>
+      <key>Comment</key>
+      <string>Enable texture labels via glObjectLabel. Requires restart for some features.</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>Boolean</string>
+      <key>Value</key>
+      <integer>0</integer>
+    </map>
   <key>RenderDelayCreation</key>
   <map>
     <key>Comment</key>
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 7b89ae4e44..99df814f32 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -364,10 +364,31 @@ void LLViewerTextureList::shutdown()
     mInitialized = false ; //prevent loading textures again.
 }
 
+namespace
+{
+
+std::string tex_name_as_string(const LLViewerFetchedTexture* image)
+{
+    if (!image->getGLTexture()) { return std::string("N/A"); }
+    return std::to_string(image->getGLTexture()->getTexName());
+}
+
+const std::string& tex_label_as_string(const LLViewerFetchedTexture* image, std::string& label)
+{
+    bool error;
+    image->getGLObjectLabel(label, error);
+    if (error) { label.assign("N/A"); }
+    return label;
+}
+
+};
+
 void LLViewerTextureList::dump()
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
-    LL_INFOS() << "LLViewerTextureList::dump()" << LL_ENDL;
+    LL_INFOS() << __FUNCTION__ << "()" << LL_ENDL;
+
+    std::string label;
     for (image_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it)
     {
         LLViewerFetchedTexture* image = *it;
@@ -378,6 +399,9 @@ void LLViewerTextureList::dump()
         << " discard " << image->getDiscardLevel()
         << " desired " << image->getDesiredDiscardLevel()
         << " http://asset.siva.lindenlab.com/" << image->getID() << ".texture"
+        << " faces " << image->getTotalNumFaces()
+        << " texname " << tex_name_as_string(image)
+        << " label \"" << tex_label_as_string(image, label) << "\""
         << LL_ENDL;
     }
 }
@@ -422,7 +446,13 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string&
 
     std::string url = "file://" + full_path;
 
-    return getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
+    LLViewerFetchedTexture* tex = getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
+    static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false);
+    if (debug_texture_label())
+    {
+        gTextureList.mNameTextureList.push_back(LLViewerTextureList::NameElement(tex, filename));
+    }
+    return tex;
 }
 
 LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url,
@@ -844,6 +874,10 @@ void LLViewerTextureList::updateImages(F32 max_time)
     //handle results from decode threads
     updateImagesCreateTextures(remaining_time);
 
+    // Label all images (if enabled)
+    updateImagesNameTextures();
+    labelAll();
+
     bool didone = false;
     for (image_list_t::iterator iter = mCallbackList.begin();
         iter != mCallbackList.end(); )
@@ -1113,6 +1147,62 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
     return create_timer.getElapsedTimeF32();
 }
 
+void LLViewerTextureList::updateImagesNameTextures()
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+    if (gGLManager.mIsDisabled) { return; }
+    static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false);
+    if (!debug_texture_label()) { return; }
+
+    static GLsizei max_length = 0;
+    if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); }
+
+    auto it = mNameTextureList.begin();
+    while (it != mNameTextureList.end()) // For ALL textures needing names
+    {
+        if (it->mTex->hasGLTexture())
+        {
+            if(it->mTex->getTexName())
+            {
+                it->mTex->setGLObjectLabel(it->mPrefix, true);
+                it = mNameTextureList.erase(it); // Assume no rename needed
+            }
+            else
+            {
+                ++it; // Not ready
+            }
+        }
+        else
+        {
+            ++it; // Not ready
+        }
+    }
+}
+
+void LLViewerTextureList::labelAll()
+{
+    LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
+    static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false);
+    if (!debug_texture_label()) { return; }
+
+    static const std::string local_prefix = "lltexlocal";
+    static const std::string other_prefix = "lltexother";
+
+    std::string label;
+    bool error;
+    for (image_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it)
+    {
+        LLViewerFetchedTexture* image = *it;
+        image->getGLObjectLabel(label, error);
+        if (!error && label.empty())
+        {
+            const S32 category = image->getGLTexture()->getCategory();
+            const std::string& new_prefix = category == LLGLTexture::LOCAL ? local_prefix : other_prefix;
+            image->setGLObjectLabel(new_prefix, true);
+        }
+    }
+}
+
 F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time)
 {
     LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
@@ -1295,6 +1385,10 @@ void LLViewerTextureList::decodeAllImages(F32 max_time)
     max_time = llmax(max_time, .001f);
     F32 create_time = updateImagesCreateTextures(max_time);
 
+    // Label all images (if enabled)
+    updateImagesNameTextures();
+    labelAll();
+
     LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. "
     << " fetch_pending " << fetch_pending
     << " create_time " << create_time
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 7c7112f4cf..6424e5c3f0 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -153,6 +153,9 @@ private:
     void updateImagesUpdateStats();
     F32  updateImagesLoadingFastCache(F32 max_time);
 
+    void updateImagesNameTextures();
+    void labelAll();
+
     void addImage(LLViewerFetchedTexture *image, ETexListType tex_type);
     void deleteImage(LLViewerFetchedTexture *image);
 
@@ -214,6 +217,14 @@ public:
     // images that have been loaded but are waiting to be uploaded to GL
     image_queue_t mCreateTextureList;
 
+    struct NameElement
+    {
+        NameElement(LLViewerFetchedTexture* tex, const std::string& prefix) : mTex(tex), mPrefix(prefix) {}
+        LLPointer<LLViewerFetchedTexture> mTex;
+        std::string mPrefix;
+    };
+    std::vector<NameElement> mNameTextureList;
+
     // images that must be downscaled quickly so we don't run out of memory
     image_queue_t mDownScaleQueue;
 
-- 
cgit v1.2.3