summaryrefslogtreecommitdiff
path: root/indra/llrender/lluiimage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/lluiimage.cpp')
-rw-r--r--indra/llrender/lluiimage.cpp211
1 files changed, 209 insertions, 2 deletions
diff --git a/indra/llrender/lluiimage.cpp b/indra/llrender/lluiimage.cpp
index dc18bf16bf..d0b6bcbf66 100644
--- a/indra/llrender/lluiimage.cpp
+++ b/indra/llrender/lluiimage.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2026, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -31,6 +31,12 @@
// Project includes
#include "lluiimage.h"
+#include <algorithm>
+
+// Static member initialization
+std::vector<LLPointer<LLUIImage> > LLUIImage::sImageList;
+size_t LLUIImage::sCleanupIndex = 0;
+bool LLUIImage::sEnableDisplayListsCollection = true;
LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image)
: mName(name),
@@ -49,6 +55,12 @@ LLUIImage::LLUIImage(const std::string& name, LLPointer<LLTexture> image)
LLUIImage::~LLUIImage()
{
delete mImageLoaded;
+
+ // Unregister from global cleanup list (sanity check)
+ // But it's supposed to be cleared already, else we wouldn't
+ // be destructing this object.
+ unregisterFromGlobalCleanup();
+ mDisplayLists.clear();
}
S32 LLUIImage::getWidth() const
@@ -63,6 +75,199 @@ S32 LLUIImage::getHeight() const
return ll_round((F32)mImage->getHeight(0) * mClipRegion.getHeight());
}
+buffer_data_list_t* LLUIImage::findDisplayList(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, bool solid_color) const
+{
+ LLImageGL* gl_image = mImage->getGLTexture();
+ if (!gl_image)
+ {
+ return nullptr;
+ }
+
+ LLVector3 ui_translation = gGL.getUITranslation();
+ LLVector3 ui_scale = gGL.getUIScale();
+ LLGLuint tex_name = gl_image->getTexName();
+
+ auto key = PackedKey::create(x, y, width, height, color, solid_color, ui_translation, ui_scale, tex_name);
+
+ auto it = mDisplayLists.find(key);
+ if (it != mDisplayLists.end())
+ {
+ it->second.last_used = std::chrono::steady_clock::now();
+ return &it->second.list;
+ }
+ return nullptr;
+}
+
+buffer_data_list_t* LLUIImage::genDisplayList(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, bool solid_color) const
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ LLImageGL* gl_image = mImage->getGLTexture();
+ if (!gl_image)
+ {
+ // Don't cache when texture hasn't been created yet
+ // draw just aborts in this case, so don't draw either.
+ return nullptr;
+ }
+
+ LLVector3 ui_translation = gGL.getUITranslation();
+ LLVector3 ui_scale = gGL.getUIScale();
+
+ // Get the GL texture name - this uniquely identifies the current texture state
+ // including discard level changes, texture recreation, etc.
+ LLGLuint tex_name = gl_image->getTexName();
+
+ auto key = PackedKey::create(x, y, width, height, color, solid_color, ui_translation, ui_scale, tex_name);
+
+ CachedDisplayList cached;
+ cached.last_used = std::chrono::steady_clock::now();
+
+ // Generate the display list by capturing the draw commands
+ gGL.beginList(&cached.list);
+
+ gl_draw_scaled_image_with_border(
+ x, y,
+ width, height,
+ mImage,
+ color,
+ solid_color,
+ mClipRegion,
+ mScaleRegion,
+ mScaleStyle == SCALE_INNER);
+
+ gGL.endList();
+
+ // Insert into cache
+ // emplace, since we only call genDisplayList if key was not found.
+ auto result = mDisplayLists.emplace(key, std::move(cached));
+
+ // Register for cleanup on first buffer creation
+ if (mDisplayLists.size() == 1)
+ {
+ sImageList.push_back(const_cast<LLUIImage*>(this));
+ }
+
+ return &result.first->second.list;
+}
+
+void LLUIImage::invalidateDisplayLists()
+{
+ mDisplayLists.clear();
+
+ unregisterFromGlobalCleanup();
+}
+
+void LLUIImage::cleanupDisplayLists()
+{
+ if (mDisplayLists.empty())
+ {
+ llassert(false); //it shouldn't be in this list
+ unregisterFromGlobalCleanup();
+ // marks current position for a recheck. Increments after cleanupDisplayLists.
+ if (sCleanupIndex > 0)
+ {
+ sCleanupIndex--;
+ }
+ else if (sImageList.empty())
+ {
+ sCleanupIndex = 0;
+ }
+ else
+ {
+ sCleanupIndex = sImageList.size() - 1;
+ }
+ return;
+ }
+
+ // Time threshold for cleaning up unused display lists (global cleanup)
+ constexpr std::chrono::seconds DISPLAY_LIST_TIMEOUT{ 2 };
+ auto now = std::chrono::steady_clock::now();
+
+ // Remove display lists that haven't been used recently
+ for (auto it = mDisplayLists.begin(); it != mDisplayLists.end(); )
+ {
+ if (now - it->second.last_used > DISPLAY_LIST_TIMEOUT)
+ {
+ it = mDisplayLists.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ // Unregister from cleanup list if all display lists were removed
+ if (mDisplayLists.empty())
+ {
+ unregisterFromGlobalCleanup();
+ // marks current position for a recheck. Increments after cleanupDisplayLists.
+ if (sCleanupIndex > 0)
+ {
+ sCleanupIndex--;
+ }
+ else if (sImageList.empty())
+ {
+ sCleanupIndex = 0;
+ }
+ else
+ {
+ sCleanupIndex = sImageList.size() - 1;
+ }
+ }
+}
+
+void LLUIImage::unregisterFromGlobalCleanup()
+{
+ auto list_it = std::find(sImageList.begin(), sImageList.end(), this);
+ if (list_it != sImageList.end())
+ {
+ // Swap with last element and pop (O(1) removal)
+ *list_it = sImageList.back();
+ sImageList.pop_back();
+ }
+}
+
+// static
+void LLUIImage::updateClass()
+{
+ if (sImageList.empty())
+ {
+ return;
+ }
+
+ // Clean up a batch of images each frame to amortize the cost
+ // ensuring all images are checked regularly
+ // Note: buffers often get obsolete in batches, perhaps
+ // increase rate of cleanup after a buffer was removed?
+ // and decrease rate if no buffer were removed and creates
+ // for a while?
+ constexpr size_t BATCH_SIZE = 8;
+ size_t images_to_process = std::min(BATCH_SIZE, sImageList.size());
+
+ for (size_t i = 0; i < images_to_process; ++i)
+ {
+ if (sCleanupIndex >= sImageList.size())
+ {
+ sCleanupIndex = 0;
+ }
+
+ sImageList[sCleanupIndex]->cleanupDisplayLists();
+ ++sCleanupIndex;
+ }
+}
+
+void LLUIImage::cleanupClass()
+{
+ std::vector<LLPointer<LLUIImage> > list_copy(sImageList);
+ sImageList.clear();
+ for (LLUIImage* image : list_copy)
+ {
+ // invalidateDisplayLists will attempt to clear sImageList
+ image->invalidateDisplayLists();
+ }
+ sCleanupIndex = 0;
+}
+
void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis,
const LLRect& rect, const LLColor4& color)
{
@@ -77,7 +282,7 @@ void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, c
}
else
{
- border_scale = (F32)rect.getWidth() / border_width;
+ border_scale = (F32)rect.getWidth() / border_width;
}
}
@@ -124,6 +329,8 @@ void LLUIImage::onImageLoaded()
{
(*mImageLoaded)();
}
+
+ invalidateDisplayLists();
}
namespace LLInitParam