summaryrefslogtreecommitdiff
path: root/indra/llrender/lluiimage.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/lluiimage.h')
-rw-r--r--indra/llrender/lluiimage.h126
1 files changed, 125 insertions, 1 deletions
diff --git a/indra/llrender/lluiimage.h b/indra/llrender/lluiimage.h
index 7dde84f295..0998ac17b4 100644
--- a/indra/llrender/lluiimage.h
+++ b/indra/llrender/lluiimage.h
@@ -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
@@ -34,10 +34,15 @@
#include "llinitparam.h"
#include "lltexture.h"
#include "llrender2dutils.h"
+#include "llvertexbuffer.h"
#include <boost/signals2.hpp>
+#include <bit>
+#include <chrono>
#include <type_traits>
+#include <unordered_map>
+#include <vector>
extern const LLColor4 UI_VERTEX_COLOR;
@@ -58,21 +63,28 @@ public:
LL_FORCE_INLINE void setClipRegion(const LLRectf& region)
{
mClipRegion = region;
+ // This happens when image becomes loaded
+ invalidateDisplayLists();
}
LL_FORCE_INLINE void setScaleRegion(const LLRectf& region)
{
mScaleRegion = region;
+ // This happens when image becomes loaded
+ invalidateDisplayLists();
}
LL_FORCE_INLINE void setScaleStyle(EScaleStyle style)
{
mScaleStyle = style;
+ // This happens when image becomes loaded
+ invalidateDisplayLists();
}
LL_FORCE_INLINE LLPointer<LLTexture> getImage() { return mImage; }
LL_FORCE_INLINE const LLPointer<LLTexture>& getImage() const { return mImage; }
+ LL_FORCE_INLINE void draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, bool solid_color) const;
LL_FORCE_INLINE void draw(S32 x, S32 y, S32 width, S32 height, const LLColor4& color = UI_VERTEX_COLOR) const;
LL_FORCE_INLINE void draw(S32 x, S32 y, const LLColor4& color = UI_VERTEX_COLOR) const;
LL_FORCE_INLINE void draw(const LLRect& rect, const LLColor4& color = UI_VERTEX_COLOR) const { draw(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color); }
@@ -85,6 +97,10 @@ public:
LL_FORCE_INLINE void drawBorder(const LLRect& rect, const LLColor4& color, S32 border_width) const { drawBorder(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color, border_width); }
LL_FORCE_INLINE void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, getWidth(), getHeight(), color, border_width); }
+ // Note: draw3D is not cached with display lists because it uses world-space rendering
+ // with dynamic transforms (gl_segmented_rect_3d_tex). These calls are infrequent and
+ // highly dynamic, making caching ineffective. The 2D UI methods benefit from caching
+ // because they're called many times per frame with the same dimensions.
void draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, const LLRect& rect, const LLColor4& color);
LL_FORCE_INLINE const std::string& getName() const { return mName; }
@@ -100,7 +116,107 @@ public:
void onImageLoaded();
+ // Global cleanup of unused display lists across all LLUIImage instances
+ // Should be called periodically (e.g., once per frame or when memory pressure is detected)
+ static void updateClass();
+ static void cleanupClass();
+
+ static void enableDisplayListsCollection(bool enable) { sEnableDisplayListsCollection = enable; }
+
protected:
+ // Packed key for identifying unique display list configurations
+ struct PackedKey
+ {
+ uint64_t position; // x and y coordinates (32 bits each)
+ uint64_t color_flags; // RGBA color (8 bits each) + solid_color flag (1 bit)
+ uint64_t dimensions; // width and height (32 bits each)
+ uint64_t translate; // UI translation (32 bits each for X and Y)
+ uint64_t scale; // UI scale (32 bits each for X and Y)
+ uint64_t tex_name; // OpenGL texture name (32 bits) + padding
+
+ constexpr bool operator==(const PackedKey& other) const
+ {
+ return position == other.position &&
+ color_flags == other.color_flags &&
+ dimensions == other.dimensions &&
+ translate == other.translate &&
+ scale == other.scale &&
+ tex_name == other.tex_name;
+ }
+
+ struct Hash
+ {
+ std::size_t operator()(const PackedKey& key) const
+ {
+ return static_cast<std::size_t>(key.position ^ key.color_flags ^
+ key.dimensions ^ key.translate ^
+ key.scale ^ key.tex_name);
+ }
+ };
+
+ // Static factory function to create PackedKey from parameters
+ static constexpr PackedKey create(S32 x, S32 y, S32 width, S32 height,
+ const LLColor4& color, bool solid_color,
+ const LLVector3& translate, const LLVector3& scale,
+ LLGLuint texture_name)
+ {
+ auto float_to_u8 = [](F32 f) -> uint8_t {
+ return static_cast<uint8_t>(llclamp(f * 255.0f, 0.0f, 255.0f));
+ };
+
+ auto float_to_bits = [](F32 f) -> uint32_t {
+ return std::bit_cast<uint32_t>(f);
+ };
+
+ uint8_t r = float_to_u8(color.mV[VRED]);
+ uint8_t g = float_to_u8(color.mV[VGREEN]);
+ uint8_t b = float_to_u8(color.mV[VBLUE]);
+ uint8_t a = float_to_u8(color.mV[VALPHA]);
+
+ uint64_t pos = (static_cast<uint64_t>(static_cast<uint32_t>(x)) << 32) |
+ static_cast<uint64_t>(static_cast<uint32_t>(y));
+
+ uint64_t col = (static_cast<uint64_t>(r) << 56) |
+ (static_cast<uint64_t>(g) << 48) |
+ (static_cast<uint64_t>(b) << 40) |
+ (static_cast<uint64_t>(a) << 32) |
+ (solid_color ? 1ULL : 0ULL);
+
+ uint64_t dim = (static_cast<uint64_t>(static_cast<uint32_t>(width)) << 32) |
+ static_cast<uint64_t>(static_cast<uint32_t>(height));
+
+ uint64_t trns = (static_cast<uint64_t>(float_to_bits(translate.mV[VX])) << 32) |
+ static_cast<uint64_t>(float_to_bits(translate.mV[VY]));
+
+ uint64_t scl = (static_cast<uint64_t>(float_to_bits(scale.mV[VX])) << 32) |
+ static_cast<uint64_t>(float_to_bits(scale.mV[VY]));
+
+ // Store full 32-bit texture name in lower 32 bits (upper 32 bits unused/zero)
+ uint64_t tex = static_cast<uint64_t>(texture_name);
+
+ return PackedKey{ pos, col, dim, trns, scl, tex };
+ }
+ };
+
+ // Cached display list for a specific configuration
+ struct CachedDisplayList
+ {
+ buffer_data_list_t list;
+ std::chrono::steady_clock::time_point last_used;
+ };
+
+ // Get a display list for the given configuration
+ buffer_data_list_t* findDisplayList(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, bool solid_color) const;
+ // Generate a new display list for the given configuration, draws immediately.
+ buffer_data_list_t* genDisplayList(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, bool solid_color) const;
+
+ // Invalidate all cached display lists (called when image properties change)
+ void invalidateDisplayLists();
+
+ // Clean up old display lists for this image (called by updateClass)
+ void cleanupDisplayLists();
+ void unregisterFromGlobalCleanup();
+
image_loaded_signal_t* mImageLoaded;
std::string mName;
@@ -110,6 +226,14 @@ protected:
EScaleStyle mScaleStyle;
mutable S32 mCachedW;
mutable S32 mCachedH;
+
+ // Display list cache - now using PackedKey directly
+ mutable std::unordered_map<PackedKey, CachedDisplayList, PackedKey::Hash> mDisplayLists;
+
+ // Track all LLUIImage cache instances for global cleanup
+ static std::vector<LLPointer<LLUIImage> > sImageList;
+ static size_t sCleanupIndex; // Round-robin cleanup position
+ static bool sEnableDisplayListsCollection;
};
#include "lluiimage.inl"