diff options
| author | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-04-08 22:53:56 +0300 |
|---|---|---|
| committer | Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> | 2026-04-09 20:48:34 +0300 |
| commit | 5f91a3faf3ef0b9d0a6c3424fff119992dc0822a (patch) | |
| tree | 31fe46c41da8a3f691cbc0e456f1b5f95f039a1a | |
| parent | 130c50cf8d4de021f510b17fd02fcac88a67c5e6 (diff) | |
#5626 LLTextBase optimization
by caching string width
| -rw-r--r-- | indra/llrender/llfontvertexbuffer.cpp | 77 | ||||
| -rw-r--r-- | indra/llrender/llfontvertexbuffer.h | 46 | ||||
| -rw-r--r-- | indra/llui/llscrollcontainer.cpp | 1 | ||||
| -rw-r--r-- | indra/llui/lltextbase.cpp | 29 | ||||
| -rw-r--r-- | indra/llui/lltextbase.h | 13 | ||||
| -rw-r--r-- | indra/newview/llexpandabletextbox.cpp | 2 | ||||
| -rw-r--r-- | indra/newview/llviewertexteditor.cpp | 12 | ||||
| -rw-r--r-- | indra/newview/pipeline.cpp | 8 |
8 files changed, 164 insertions, 24 deletions
diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index a223509d30..2a0115265f 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -237,3 +237,80 @@ void LLFontVertexBuffer::renderBuffers() gGL.popUIMatrix(); } +// LLFontWidthBuffer +bool LLFontWidthBuffer::sEnableBufferCollection = true; + +LLFontWidthBuffer::LLFontWidthBuffer() +{ +} + +LLFontWidthBuffer::~LLFontWidthBuffer() +{ +} + +void LLFontWidthBuffer::reset() +{ + mLastFont = nullptr; + mLastOffset = 0; + mLastMaxChars = 0; + mLastNoPadding = false; + mWidth = -1.f; + mLastScaleX = 1.f; + mLastScaleY = 1.f; + mLastVertDPI = 0.f; + mLastHorizDPI = 0.f; + mLastResGeneration = 0; + mLastFontCacheGen = 0; +} + +F32 LLFontWidthBuffer::getWidth( + const LLFontGL* fontp, + const llwchar* wchars, + S32 begin_offset, + S32 max_chars, + bool no_padding) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; + if (!fontp || !wchars) + { + return 0.f; + } + + if (!sEnableBufferCollection) + { + return fontp->getWidthF32(wchars, begin_offset, max_chars, no_padding); + } + + // Check if we can use cached width + bool needs_recalc = (mWidth < 0.f) + || (mLastFont != fontp) + || (mLastOffset != begin_offset) + || (mLastMaxChars != max_chars) + || (mLastNoPadding != no_padding) + || (mLastScaleX != LLFontGL::sScaleX) + || (mLastScaleY != LLFontGL::sScaleY) + || (mLastVertDPI != LLFontGL::sVertDPI) + || (mLastHorizDPI != LLFontGL::sHorizDPI) + || (mLastResGeneration != LLFontGL::sResolutionGeneration) + || (mLastFontCacheGen != fontp->getCacheGeneration()); + + if (needs_recalc) + { + // Calculate width using the font + mWidth = fontp->getWidthF32(wchars, begin_offset, max_chars, no_padding); + + // Cache the parameters + mLastFont = fontp; + mLastOffset = begin_offset; + mLastMaxChars = max_chars; + mLastNoPadding = no_padding; + mLastScaleX = LLFontGL::sScaleX; + mLastScaleY = LLFontGL::sScaleY; + mLastVertDPI = LLFontGL::sVertDPI; + mLastHorizDPI = LLFontGL::sHorizDPI; + mLastResGeneration = LLFontGL::sResolutionGeneration; + mLastFontCacheGen = fontp->getCacheGeneration(); + } + + return mWidth; +} diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index a9e1e2337c..94b833d227 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -32,6 +32,11 @@ class LLVertexBufferData; +// Rendering fonts is expensive, this class is intended to store +// vertex buffers for rendered text, so that they can be reused. +// LLFontVertexBuffer tracks font and rendering parameters, but +// expects caller to track text changes and call reset() when +// text changes. class LLFontVertexBuffer { public: @@ -127,4 +132,45 @@ private: static bool sEnableBufferCollection; }; +// Extracting width from a font is expensive, and due to +// mechanics of font rendering, we need width separately +// and usually before rendering. +// LLFontWidthBuffer tracks font and rendering parameters, +// but expects caller to track text changes and call reset() +// when text changes. +class LLFontWidthBuffer +{ +public: + LLFontWidthBuffer(); + ~LLFontWidthBuffer(); + + void reset(); + + F32 getWidth(const LLFontGL* fontp, + const llwchar* wchars, + S32 begin_offset, + S32 max_chars, + bool no_padding); + + static void enableBufferCollection(bool enable) { sEnableBufferCollection = enable; } +private: + const LLFontGL* mLastFont = nullptr; + S32 mLastOffset = 0; + S32 mLastMaxChars = 0; + bool mLastNoPadding = false; + F32 mWidth = -1.f; + + // LLFontGL's values that affect width calculation + F32 mLastScaleX = 1.f; + F32 mLastScaleY = 1.f; + F32 mLastVertDPI = 0.f; + F32 mLastHorizDPI = 0.f; + S32 mLastResGeneration = 0; + + // Cache generation tracking + S32 mLastFontCacheGen = 0; + + static bool sEnableBufferCollection; +}; + #endif diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index df99c4f636..e36fd45bb4 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -480,6 +480,7 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height void LLScrollContainer::draw() { + LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; static LLUICachedControl<S32> scrollbar_size_control ("UIScrollbarSize", 0); S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index c084b400f6..9fcf2a5f10 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1640,7 +1640,7 @@ void LLTextBase::deselect() bool LLTextBase::getSpellCheck() const { - return (LLSpellChecker::getUseSpellCheck()) && (!mReadOnly) && (mSpellCheck); + return (!mReadOnly) && (LLSpellChecker::getUseSpellCheck()) && (mSpellCheck); } const std::string& LLTextBase::getSuggestion(U32 index) const @@ -2855,7 +2855,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, bool round, line_seg_iter != mSegments.end(); ++line_seg_iter, line_seg_offset = 0) { - const LLTextSegmentPtr segmentp = *line_seg_iter; + LLTextSegmentPtr segmentp = *line_seg_iter; S32 segment_line_start = segmentp->getStart() + line_seg_offset; S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd) - segment_line_start; @@ -2946,7 +2946,7 @@ LLRect LLTextBase::getDocRectFromDocIndex(S32 pos) const while(line_seg_iter != mSegments.end()) { - const LLTextSegmentPtr segmentp = *line_seg_iter; + LLTextSegmentPtr segmentp = *line_seg_iter; if (line_seg_iter == cursor_seg_iter) { @@ -3497,8 +3497,8 @@ LLStyleSP LLTextSegment::cloneStyle(LLTextBase& target, const LLStyle* source) } -bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { width = 0; height = 0; return false; } -bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const +bool LLTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { width = 0; height = 0; return false; } +bool LLTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) { F32 fwidth = 0; bool result = getDimensionsF32(first_char, num_chars, fwidth, height); @@ -3595,6 +3595,7 @@ F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selec mFontBufferPreSelection.reset(); mFontBufferSelection.reset(); mFontBufferPostSelection.reset(); + mFontWidthBuffer.reset(); } return draw_rect.mLeft; } @@ -3620,6 +3621,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele mFontBufferPreSelection.reset(); mFontBufferSelection.reset(); mFontBufferPostSelection.reset(); + mFontWidthBuffer.reset(); } const LLFontGL* font = mStyle->getFont(); @@ -3851,17 +3853,19 @@ LLTextSegmentPtr LLNormalTextSegment::clone(LLTextBase& target) const return new LLNormalTextSegment(sp, mStart, mEnd, target); } -bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { height = 0; width = 0; if (num_chars > 0 && (mStart + first_char >= 0)) { height = mFontHeight; - const LLWString &text = getWText(); - // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true); + + const LLWString& text = getWText(); + const LLFontGL* font = mStyle->getFont(); + width += mFontWidthBuffer.getWidth(font, text.c_str(), mStart + first_char, num_chars, true); } + // if last character is a newline, then return true, forcing line break return false; } @@ -3943,6 +3947,7 @@ void LLNormalTextSegment::updateLayout(const class LLTextBase& editor) mFontBufferPreSelection.reset(); mFontBufferSelection.reset(); mFontBufferPostSelection.reset(); + mFontWidthBuffer.reset(); } void LLNormalTextSegment::dump() const @@ -4094,7 +4099,7 @@ LLTextSegmentPtr LLInlineViewSegment::clone(LLTextBase& target) const return nullptr; } -bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +bool LLInlineViewSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { if (first_char == 0 && num_chars == 0) { @@ -4186,7 +4191,7 @@ LLTextSegmentPtr LLLineBreakTextSegment::clone(LLTextBase& target) const copy->mFontHeight = mFontHeight; return copy; } -bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +bool LLLineBreakTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { width = 0; height = mFontHeight; @@ -4223,7 +4228,7 @@ LLTextSegmentPtr LLImageTextSegment::clone(LLTextBase& target) const static const S32 IMAGE_HPAD = 3; // virtual -bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const +bool LLImageTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { width = 0; height = mStyle->getFont()->getLineHeight(); diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 21134fd7c9..9206b3facf 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -68,10 +68,10 @@ public: virtual LLTextSegmentPtr clone(LLTextBase& terget) const { return new LLTextSegment(mStart, mEnd); } static LLStyleSP cloneStyle(LLTextBase& target, const LLStyle* source); - bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; + bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height); bool getPermitsEmoji() const { return mPermitsEmoji; }; - virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + virtual bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height); virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; /** @@ -139,7 +139,7 @@ public: virtual ~LLNormalTextSegment(); /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height); /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; /*virtual*/ void updateLayout(const class LLTextBase& editor); @@ -182,6 +182,7 @@ protected: LLFontVertexBuffer mFontBufferPreSelection; LLFontVertexBuffer mFontBufferSelection; LLFontVertexBuffer mFontBufferPostSelection; + LLFontWidthBuffer mFontWidthBuffer; S32 mLastGeneration = -1; }; @@ -254,7 +255,7 @@ public: ~LLInlineViewSegment(); /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height); /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; /*virtual*/ void updateLayout(const class LLTextBase& editor); /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -280,7 +281,7 @@ public: LLLineBreakTextSegment(S32 pos); ~LLLineBreakTextSegment(); /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height); S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); @@ -295,7 +296,7 @@ public: ~LLImageTextSegment(); /*virtual*/ LLTextSegmentPtr clone(LLTextBase& target) const; - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const; + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height); S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 char_offset, S32 max_chars, S32 line_ind) const; F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect); diff --git a/indra/newview/llexpandabletextbox.cpp b/indra/newview/llexpandabletextbox.cpp index 5c46eb9d80..41ae47b645 100644 --- a/indra/newview/llexpandabletextbox.cpp +++ b/indra/newview/llexpandabletextbox.cpp @@ -52,7 +52,7 @@ public: return copy; } - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) { // more label always spans width of text box if (num_chars == 0) diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 210cd62d6f..95e34b4df1 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -189,7 +189,12 @@ public: return new LLEmbeddedItemSegment(mStart, mImage, mItem, *editor); } - /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const + /*virtual*/ bool getDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) + { + return getSegmentDimensionsF32(first_char, num_chars, width, height); + } + + inline bool getSegmentDimensionsF32(S32 first_char, S32 num_chars, F32& width, S32& height) const { if (num_chars == 0) { @@ -213,8 +218,9 @@ public: } else { - S32 width, height; - getDimensions(mStart, 1, width, height); + F32 width; + S32 height; + getSegmentDimensionsF32(mStart, 1, width, height); if (width > num_pixels) { return 0; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index c9d53bbcbc..4ef88f5deb 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -609,7 +609,9 @@ void LLPipeline::init() { cntrl_ptr->getCommitSignal()->connect([](LLControlVariable* control, const LLSD& value, const LLSD& previous) { - LLFontVertexBuffer::enableBufferCollection(control->getValue().asBoolean()); + bool enable_buffers = control->getValue().asBoolean(); + LLFontVertexBuffer::enableBufferCollection(enable_buffers); + LLFontWidthBuffer::enableBufferCollection(enable_buffers); }); } } @@ -1146,7 +1148,9 @@ void LLPipeline::refreshCachedSettings() LLVOAvatar::updateImpostorRendering(LLVOAvatar::sMaxNonImpostors); } - LLFontVertexBuffer::enableBufferCollection(gSavedSettings.getBOOL("CollectFontVertexBuffers")); + bool enable_buffers = gSavedSettings.getBOOL("CollectFontVertexBuffers"); + LLFontVertexBuffer::enableBufferCollection(enable_buffers); + LLFontWidthBuffer::enableBufferCollection(enable_buffers); } void LLPipeline::releaseGLBuffers() |
