summaryrefslogtreecommitdiff
path: root/indra/llrender
diff options
context:
space:
mode:
authorErik Kundiman <erik@megapahit.org>2026-08-18 13:57:14 +0800
committerErik Kundiman <erik@megapahit.org>2026-08-18 13:57:14 +0800
commit2fa8281b2d8ce8bdbcacf139ec674480da6c9d81 (patch)
treeffb343fee97f21942123fd322d88fea3ef1a4811 /indra/llrender
parent742d802f073e81abc6d4cd512e58a4d03b414b83 (diff)
parent214fa765986b4c1e1de1b917581f8508278abd8e (diff)
Merge branch '26.3'
Diffstat (limited to 'indra/llrender')
-rw-r--r--indra/llrender/llfontfreetype.cpp43
-rw-r--r--indra/llrender/llfontvertexbuffer.cpp77
-rw-r--r--indra/llrender/llfontvertexbuffer.h46
-rw-r--r--indra/llrender/llimagegl.cpp2
-rw-r--r--indra/llrender/llrender.cpp14
-rw-r--r--indra/llrender/llshadermgr.cpp19
6 files changed, 180 insertions, 21 deletions
diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp
index 91898e6de1..357dfc8ea8 100644
--- a/indra/llrender/llfontfreetype.cpp
+++ b/indra/llrender/llfontfreetype.cpp
@@ -990,30 +990,39 @@ namespace ll
U8 const* LLFontManager::loadFont( std::string const &aFilename, long &a_Size)
{
- a_Size = 0;
- std::map< std::string, std::shared_ptr<ll::fonts::LoadedFont> >::iterator itr = m_LoadedFonts.find( aFilename );
- if( itr != m_LoadedFonts.end() )
+ try
{
- ++itr->second->mRefs;
- // A possible overflow cannot happen here, as it is asserted that the size is less than std::numeric_limits<long>::max() a few lines below.
- a_Size = static_cast<long>(itr->second->mSize);
- return reinterpret_cast<U8 const*>(itr->second->mAddress.c_str());
- }
+ a_Size = 0;
+ std::map< std::string, std::shared_ptr<ll::fonts::LoadedFont> >::iterator itr = m_LoadedFonts.find(aFilename);
+ if (itr != m_LoadedFonts.end())
+ {
+ ++itr->second->mRefs;
+ // A possible overflow cannot happen here, as it is asserted that the size is less than std::numeric_limits<long>::max() a few lines below.
+ a_Size = static_cast<long>(itr->second->mSize);
+ return reinterpret_cast<U8 const*>(itr->second->mAddress.c_str());
+ }
- auto strContent = LLFile::getContents(aFilename);
+ auto strContent = LLFile::getContents(aFilename);
- if( strContent.empty() )
- return nullptr;
+ if (strContent.empty())
+ return nullptr;
- // For fontconfig a type of long is required, std::string::size() returns size_t. I think it is safe to limit this to 2GiB and not support fonts that huge (can that even be a thing?)
- llassert_always( strContent.size() < std::numeric_limits<long>::max() );
+ // For fontconfig a type of long is required, std::string::size() returns size_t. I think it is safe to limit this to 2GiB and not support fonts that huge (can that even be a thing?)
+ llassert_always(strContent.size() < std::numeric_limits<long>::max());
- a_Size = static_cast<long>(strContent.size());
+ a_Size = static_cast<long>(strContent.size());
- auto pCache = std::make_shared<ll::fonts::LoadedFont>( aFilename, strContent, a_Size );
- itr = m_LoadedFonts.insert( std::make_pair( aFilename, pCache ) ).first;
+ auto pCache = std::make_shared<ll::fonts::LoadedFont>(aFilename, strContent, a_Size);
+ itr = m_LoadedFonts.insert(std::make_pair(aFilename, pCache)).first;
- return reinterpret_cast<U8 const*>(itr->second->mAddress.c_str());
+ return reinterpret_cast<U8 const*>(itr->second->mAddress.c_str());
+ }
+ catch (const std::bad_alloc&)
+ {
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS() << "Failed to load font. Out of memory." << LL_ENDL;
+ }
+ return nullptr;
}
void LLFontManager::unloadAllFonts()
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/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 4901cb423b..1942ddb648 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -1955,7 +1955,7 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
LLGLint is_compressed = 0;
if (compressed_ok)
{
- glGetTexLevelParameteriv(mTarget, is_compressed, GL_TEXTURE_COMPRESSED, (GLint*)&is_compressed);
+ glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED, (GLint*)&is_compressed);
}
//-----------------------------------------------------------------------------------------------
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 658947d531..bb98696108 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -1788,7 +1788,16 @@ LLVertexBuffer* LLRender::genBuffer(U32 attribute_mask, S32 count)
LLVertexBuffer * vb = new LLVertexBuffer(attribute_mask);
vb->allocateBuffer(count, 0);
- vb->setBuffer();
+ // Non-Apple path uses glBufferSubData inside setXxxData, so the VBO
+ // must already be bound. On Apple, the VBO is lazily created in
+ // _unmapBuffer (LLAppleVBOPool); calling setBuffer() here would bind
+ // mGLBuffer == 0 and then setupVertexBuffer would issue
+ // glVertexAttribIPointer with a non-null offset against no bound
+ // GL_ARRAY_BUFFER -> GL_INVALID_OPERATION in core profile.
+ if (!gGLManager.mIsApple)
+ {
+ vb->setBuffer();
+ }
vb->setPositionData(mVerticesp.get());
@@ -1804,6 +1813,9 @@ LLVertexBuffer* LLRender::genBuffer(U32 attribute_mask, S32 count)
if(gGLManager.mIsApple && LLVertexBuffer::getVertexBufferMode() == 0)
{
+ // unmapBuffer creates the GL buffer, uploads, and leaves it bound,
+ // drawBuffer's later setBuffer() then runs setupVertexBuffer against
+ // a valid VBO.
vb->unmapBuffer();
}
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index f50ce26793..fae2bd5034 100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1026,8 +1026,23 @@ void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version,
llifstream instream(meta_out_path, std::ifstream::in | std::ifstream::binary);
LLSD in_data;
- // todo: this is likely very expensive to parse, should use binary
- LLSDSerialize::fromBinary(in_data, instream, LLSDSerialize::SIZE_UNLIMITED);
+ try
+ {
+ LLSDSerialize::fromBinary(in_data, instream, LLSDSerialize::SIZE_UNLIMITED);
+ }
+ catch( std::bad_alloc& )
+ {
+ // Try to get a bit more memory back before we try to clear the cache.
+ in_data.clear();
+ // Just in case it was somehow the cause, clear cache.
+ clearShaderCache();
+ // If user run out of memory this early in init,
+ // we don't want to keep going just to crash again.
+ // Notify user and close.
+ LLError::LLUserWarningMsg::showOutOfMemory();
+ LL_ERRS("ShaderMgr") << "Failed to parse shader cache metadata, potentially due to size. Purged cache." << LL_ENDL;
+ return;
+ }
instream.close();
if (old_cache_version == current_cache_version