summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llrender/llimagegl.cpp84
-rw-r--r--indra/llrender/llimagegl.h16
-rw-r--r--indra/llrender/llrender.cpp14
-rw-r--r--indra/newview/app_settings/settings.xml144
-rw-r--r--indra/newview/llface.cpp30
-rw-r--r--indra/newview/llface.h7
-rw-r--r--indra/newview/llsurface.cpp14
-rw-r--r--indra/newview/llsurface.h6
-rw-r--r--indra/newview/lltexturefetch.cpp12
-rw-r--r--indra/newview/lltexturefetch.h3
-rw-r--r--indra/newview/llviewercontrol.cpp21
-rw-r--r--indra/newview/llviewertexture.cpp276
-rw-r--r--indra/newview/llviewertexture.h50
-rw-r--r--indra/newview/llviewertexturelist.cpp174
-rw-r--r--indra/newview/llviewertexturelist.h5
-rw-r--r--indra/newview/llvlcomposition.cpp18
16 files changed, 701 insertions, 173 deletions
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index 4a3d32c7ff..d79d13dc8b 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -66,8 +66,8 @@ static LLMutex sTexMemMutex;
static std::unordered_map<U32, U64> sTextureAllocs;
static U64 sTextureBytes = 0;
-// track a texture alloc on the currently bound texture.
-// asserts that no currently tracked alloc exists
+// Per-mip upload paths call this once per level; only free_tex_image
+// removes a texture's accounting entirely.
void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 intformat, U32 count)
{
U32 texUnit = gGL.getCurrentTexUnitIndex();
@@ -80,15 +80,46 @@ void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 intformat, U32
sTexMemMutex.lock();
- // it is a precondition that no existing allocation exists for this texture
- llassert(sTextureAllocs.find(texName) == sTextureAllocs.end());
-
- sTextureAllocs[texName] = size;
+ auto iter = sTextureAllocs.find(texName);
+ if (iter != sTextureAllocs.end())
+ {
+ iter->second += size;
+ }
+ else
+ {
+ sTextureAllocs[texName] = size;
+ }
sTextureBytes += size;
sTexMemMutex.unlock();
}
+// Add mip 1..N bytes to existing accounting. Use after glGenerateMipmap.
+void LLImageGLMemory::account_extra_mip_bytes(U32 base_width, U32 base_height, U32 intformat)
+{
+ U64 extra = 0;
+ U32 w = base_width;
+ U32 h = base_height;
+ while (w > 1 || h > 1)
+ {
+ w = w > 1 ? w >> 1 : 1;
+ h = h > 1 ? h >> 1 : 1;
+ extra += LLImageGL::dataFormatBytes(intformat, w, h);
+ }
+
+ U32 texUnit = gGL.getCurrentTexUnitIndex();
+ U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
+
+ sTexMemMutex.lock();
+ auto iter = sTextureAllocs.find(texName);
+ if (iter != sTextureAllocs.end())
+ {
+ iter->second += extra;
+ sTextureBytes += extra;
+ }
+ sTexMemMutex.unlock();
+}
+
// track texture free on given texName
void LLImageGLMemory::free_tex_image(U32 texName)
{
@@ -838,7 +869,7 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32
mMipLevels = wpo2(llmax(w, h));
//use legacy mipmap generation mode (note: making this condional can cause rendering issues)
- // -- but making it not conditional triggers deprecation warnings when core profile is enabled
+ // - but making it not conditional triggers deprecation warnings when core profile is enabled
// (some rendering issues while core profile is enabled are acceptable at this point in time)
if (!LLRender::sGLCoreProfile)
{
@@ -864,6 +895,7 @@ bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32
{
LL_PROFILE_GPU_ZONE("generate mip map");
glGenerateMipmap(mTarget);
+ account_extra_mip_bytes(w, h, mFormatInternal);
}
stop_glerror();
}
@@ -1461,7 +1493,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
LL_PROFILE_ZONE_NUM(width);
LL_PROFILE_ZONE_NUM(height);
- free_cur_tex_image();
+ // Release prior accounting only on the base mip; per-mip iteration
+ // accumulates the rest via the additive alloc_tex_image.
+ if (miplevel == 0)
+ {
+ free_cur_tex_image();
+ }
const bool use_sub_image = should_stagger_image_set(compress);
if (!use_sub_image)
{
@@ -2039,6 +2076,28 @@ S32 LLImageGL::getWidth(S32 discard_level) const
return width;
}
+// static
+S32 LLImageGL::dimDerivedMaxDiscard(S32 width, S32 height)
+{
+ if (width <= 0 || height <= 0)
+ {
+ return 0;
+ }
+ // max(w,h) - min() caps short on rectangular textures
+ // (1024x512 reaches 1x1 at discard 10, not 9).
+ return (S32)floorf(log2f((F32)llmax(width, height)));
+}
+
+void LLImageGL::stampBound() const
+{
+ // Skip the store on same-frame re-binds - bindFast is per-draw and
+ // would dirty this cache line per bind per texture otherwise.
+ if (mLastBindTime != sLastFrameTime)
+ {
+ mLastBindTime = sLastFrameTime;
+ }
+}
+
S64 LLImageGL::getBytes(S32 discard_level) const
{
if (discard_level < 0)
@@ -2468,7 +2527,12 @@ bool LLImageGL::scaleDown(S32 desired_discard)
return false;
}
- desired_discard = llmin(desired_discard, mMaxDiscardLevel);
+ // GL pyramid reaches 1x1 regardless of codec levels;
+ // mMaxDiscardLevel is hardcapped at MAX_DISCARD_LEVEL.
+ S32 dim_max_discard = (mWidth > 0 && mHeight > 0)
+ ? dimDerivedMaxDiscard(mWidth, mHeight)
+ : (S32)mMaxDiscardLevel;
+ desired_discard = llmin(desired_discard, dim_max_discard);
if (desired_discard <= mCurrentDiscardLevel)
{
@@ -2501,6 +2565,7 @@ bool LLImageGL::scaleDown(S32 desired_discard)
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap");
gGL.getTexUnit(0)->bind(this);
glGenerateMipmap(mTarget);
+ account_extra_mip_bytes(desired_width, desired_height, mFormatInternal);
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
}
}
@@ -2546,6 +2611,7 @@ bool LLImageGL::scaleDown(S32 desired_discard)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("scaleDown - glGenerateMipmap");
glGenerateMipmap(mTarget);
+ account_extra_mip_bytes(desired_width, desired_height, mFormatInternal);
}
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
index 6b4492c09e..a02c320738 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -51,6 +51,11 @@ class LLWindow;
namespace LLImageGLMemory
{
void alloc_tex_image(U32 width, U32 height, U32 intformat, U32 count);
+
+ // Add mip 1..N bytes to existing accounting. Call after glGenerateMipmap
+ // when only the base mip was accounted; without this the bytes counter
+ // undercounts mipmap-generated textures by ~25%.
+ void account_extra_mip_bytes(U32 base_width, U32 base_height, U32 intformat);
void free_tex_image(U32 texName);
void free_tex_images(U32 count, const U32* texNames);
void free_cur_tex_image();
@@ -151,6 +156,15 @@ public:
S32 getDiscardLevel() const { return mCurrentDiscardLevel; }
S32 getMaxDiscardLevel() const { return mMaxDiscardLevel; }
+ // floor(log2(max(w, h))) - deepest GL pyramid level (down to 1x1).
+ // Returns 0 for non-positive inputs.
+ static S32 dimDerivedMaxDiscard(S32 width, S32 height);
+
+ // Record the wall-clock bind time - every bind path that touches a
+ // streaming-managed texture must call this, or the staleness signal
+ // sees the texture as never-bound and ramps it toward eviction.
+ void stampBound() const;
+
// override the current discard level
// should only be used for local textures where you know exactly what you're doing
void setDiscardLevel(S32 level) { mCurrentDiscardLevel = level; }
@@ -224,7 +238,7 @@ public:
public:
// Various GL/Rendering options
S64Bytes mTextureMemory;
- mutable F32 mLastBindTime; // last time this was bound, by discard level
+ mutable F32 mLastBindTime = 0.f; // wall-clock time at last stampBound; drives streaming staleness
private:
U32 createPickMask(S32 pWidth, S32 pHeight);
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index 1a3a499b20..696a0d145f 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -197,6 +197,9 @@ void LLTexUnit::bindFast(LLTexture* texture)
glActiveTexture(GL_TEXTURE0 + mIndex);
gGL.mCurrTextureUnitIndex = mIndex;
mCurrTexture = gl_tex->getTexName();
+ // bindFast bypasses updateBindStats(); stamp directly so the staleness
+ // signal sees per-frame use of batched textures.
+ gl_tex->stampBound();
if (!mCurrTexture)
{
LL_PROFILE_ZONE_NAMED("MISSING TEXTURE");
@@ -249,11 +252,17 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
setTextureFilteringOption(gl_tex->mFilterOption);
}
}
+ else
+ {
+ // Already current - still being used, keep it fresh.
+ gl_tex->stampBound();
+ }
}
else
{
//if deleted, will re-generate it immediately
texture->forceImmediateUpdate() ;
+ gl_tex->stampBound();
gl_tex->forceUpdateBindStats() ;
return texture->bindDefaultImage(mIndex);
@@ -325,6 +334,11 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32
stop_glerror();
}
}
+ else
+ {
+ // Already current - still being used, keep it fresh.
+ texture->stampBound();
+ }
stop_glerror();
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9de8892c5e..0f1b704dc9 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9221,7 +9221,7 @@
<key>RenderReflectionDetail</key>
<map>
<key>Comment</key>
- <string>DEPRECATED -- use RenderTransparentWater and RenderReflectionProbeDetail -- Detail of reflection render pass.</string>
+ <string>DEPRECATED - use RenderTransparentWater and RenderReflectionProbeDetail - Detail of reflection render pass.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -11819,30 +11819,152 @@
<key>TextureChannelPriority</key>
<map>
<key>Comment</key>
- <string>Per-channel texture streaming aggressiveness. X=normals, Y=diffuse, Z=specular/metallic, W=emissive. 1.0=baseline, higher=more aggressive downrez.</string>
+ <string>Per-channel exponent on the combined discard factor. X=normals, Y=diffuse, Z=spec, W=emissive. 1.0 = baseline; lower = more aggressive. Driven by the RenderTextureQuality preset.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Vector4</string>
<key>Value</key>
<array>
- <real>5</real>
- <real>7.5</real>
- <real>20</real>
- <real>7.5</real>
+ <real>1.0</real>
+ <real>0.75</real>
+ <real>0.5</real>
+ <real>0.75</real>
</array>
</map>
- <key>TextureCameraBoost</key>
+ <key>TextureMaxDiscardOverride</key>
<map>
<key>Comment</key>
- <string>Amount to boost resolution of textures that are important to the camera.</string>
+ <string>When non-zero, overrides the per-texture codec-derived max discard cap. 0 = use codec-reported levels. Higher lets the streaming math push past the codec ceiling; scaleDown handles the GL side.</string>
<key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
<integer>0</integer>
+ </map>
+ <key>TextureMemoryPressureRampRate</key>
+ <map>
+ <key>Comment</key>
+ <string>Feedback rate (per second) for the VRAM-pressure factor (0..1). Higher = faster convergence on the budget; lower = gentler.</string>
+ <key>Persist</key>
+ <integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>8.0</real>
+ <real>3.0</real>
+ </map>
+ <key>TextureMemoryPressureBackoffStart</key>
+ <map>
+ <key>Comment</key>
+ <string>Fraction of the VRAM target at which the pressure ramp starts (0..1). Lower = earlier headroom-building; 1.0 disables backoff (ramp only above target).</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.85</real>
+ </map>
+ <key>TextureTerrainDistanceFloor</key>
+ <map>
+ <key>Comment</key>
+ <string>Minimum distance factor for BOOST_TERRAIN textures. Keeps combined &gt; 0 so VRAM pressure can evict terrain. Lower = higher idle quality, less pressure response.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.01</real>
+ </map>
+ <key>TextureTerrainCoverageFraction</key>
+ <map>
+ <key>Comment</key>
+ <string>Synthetic on-screen coverage fraction for BOOST_TERRAIN textures (no faces are registered). Higher = higher idle quality, less pressure response.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.99</real>
+ </map>
+ <key>TextureAgentAvatarBoost</key>
+ <map>
+ <key>Comment</key>
+ <string>Quality boost (0..1) for textures on the agent's avatar (rigged mesh / animated objects). Lower = higher quality. Preference, not exemption - pressure can still evict.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.5</real>
+ </map>
+ <key>TextureBackgroundFactorRatePerSec</key>
+ <map>
+ <key>Comment</key>
+ <string>Per-second ramp rate of the background-window discard floor (0..1). Snaps to 0 in foreground. Default 0.011 ~ 90s to saturate.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.011</real>
+ </map>
+ <key>TextureDistanceDiscardPower</key>
+ <map>
+ <key>Comment</key>
+ <string>Exponent on the distance factor (face_distance / draw_distance). 1.0 = linear; lower = textures hit max discard sooner with distance. Default 0.5 = sqrt.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>0.5</real>
</map>
+ <key>TextureSizeDiscardPower</key>
+ <map>
+ <key>Comment</key>
+ <string>Exponent on the on-screen size factor (1 - coverage). 1.0 = linear; lower = small-on-screen textures attenuate sooner.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
+ <key>TextureStalenessIntervalSeconds</key>
+ <map>
+ <key>Comment</key>
+ <string>Seconds per staleness step. Per-interval increment is 1/max_discard so any texture saturates after interval * max_discard seconds idle.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>5.0</real>
+ </map>
+ <key>TextureBindDecaySeconds</key>
+ <map>
+ <key>Comment</key>
+ <string>Grace seconds after a bind during which the staleness factor stays at 0. Prevents intermittently-bound textures from ramping. 0 disables the grace period.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>5.0</real>
+ </map>
+ <key>TextureFetchPressureScale</key>
+ <map>
+ <key>Comment</key>
+ <string>Pending-fetch divisor for the bias floor: bias floor = 1 + clamp(pending / scale, 0, 3). Lower = bias rises sooner under load floods.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1000.0</real>
+ </map>
+
<key>TextureDecodeDisabled</key>
<map>
<key>Comment</key>
@@ -16383,7 +16505,7 @@
<key>EmulateCoreCount</key>
<map>
<key>Comment</key>
- <string>For debugging -- number of cores to restrict the main process to, or 0 for no limit. Requires restart.</string>
+ <string>For debugging - number of cores to restrict the main process to, or 0 for no limit. Requires restart.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -16537,7 +16659,7 @@
<key>MultiModeDoubleClickFolder</key>
<map>
<key>Comment</key>
- <string>Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 – stays in current floater but switches to SFV)</string>
+ <string>Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 - stays in current floater but switches to SFV)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index 018d4c4bba..e34bea63ef 100644
--- a/indra/newview/llface.cpp
+++ b/indra/newview/llface.cpp
@@ -1577,7 +1577,7 @@ bool LLFace::getGeometryVolume(const LLVolume& volume,
skin = mSkinInfo;
}
- //TODO -- cache this (check profile marker above)?
+ //TODO - cache this (check profile marker above)?
glm::mat4 m = glm::make_mat4((F32*)skin->mBindShapeMatrix.getF32ptr());
m = glm::transpose(glm::inverse(m));
mat_normal.loadu(glm::value_ptr(m));
@@ -2265,7 +2265,18 @@ F32 LLFace::getTextureVirtualSize()
face_area = mPixelArea / llclamp(texel_area, 0.015625f, 128.f);
}
- face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area);
+ // Diffuse source area as the dim-aware hint for adjustPixelArea.
+ S32 source_area = 0;
+ if (mTexture[LLRender::DIFFUSE_MAP].notNull())
+ {
+ S32 sw = mTexture[LLRender::DIFFUSE_MAP]->getFullWidth();
+ S32 sh = mTexture[LLRender::DIFFUSE_MAP]->getFullHeight();
+ if (sw > 0 && sh > 0)
+ {
+ source_area = sw * sh;
+ }
+ }
+ face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_area, source_area);
if(face_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping.
{
if(mImportanceToCamera > LEAST_IMPORTANCE_FOR_LARGE_IMAGE && mTexture[LLRender::DIFFUSE_MAP].notNull() && mTexture[LLRender::DIFFUSE_MAP]->isLargeImage())
@@ -2389,6 +2400,7 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
F32 dist = lookAt.getLength3().getF32();
dist = llmax(dist-size.getLength3().getF32(), 0.001f);
+ mDistanceToCamera = dist;
lookAt.normalize3fast() ;
@@ -2513,8 +2525,16 @@ F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
}
//static
-F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_area)
+F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_area, S32 source_area)
{
+ // Dim-aware floor: source_area/256 lowers the "large but unimportant"
+ // clamp proportionally so smaller sources can be pushed past discard 4.
+ F32 large_floor = (F32)LLViewerTexture::sMinLargeImageSize;
+ if (source_area > 0)
+ {
+ large_floor = llmin(large_floor, (F32)source_area / 256.f);
+ }
+
if(pixel_area > LLViewerTexture::sMaxSmallImageSize)
{
if(importance < LEAST_IMPORTANCE) //if the face is not important, do not load hi-res.
@@ -2522,11 +2542,11 @@ F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_area)
static const F32 MAX_LEAST_IMPORTANCE_IMAGE_SIZE = 128.0f * 128.0f ;
pixel_area = llmin(pixel_area * 0.5f, MAX_LEAST_IMPORTANCE_IMAGE_SIZE) ;
}
- else if(pixel_area > LLViewerTexture::sMinLargeImageSize) //if is large image, shrink face_area by considering the partial overlapping.
+ else if(pixel_area > large_floor) //if is large image, shrink face_area by considering the partial overlapping.
{
if(importance < LEAST_IMPORTANCE_FOR_LARGE_IMAGE)//if the face is not important, do not load hi-res.
{
- pixel_area = (F32)LLViewerTexture::sMinLargeImageSize ;
+ pixel_area = large_floor ;
}
}
}
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 6e9d23c3a2..71ba3d0f2f 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -242,7 +242,9 @@ private:
bool calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ;
public:
static F32 calcImportanceToCamera(F32 to_view_dir, F32 dist);
- static F32 adjustPixelArea(F32 importance, F32 pixel_area) ;
+ // source_area > 0 lowers the "large but unimportant" floor for
+ // moderate sources; 0 keeps the legacy sMinLargeImageSize floor.
+ static F32 adjustPixelArea(F32 importance, F32 pixel_area, S32 source_area = 0) ;
public:
@@ -251,6 +253,9 @@ public:
LLVector2 mTexExtents[2];
F32 mDistance;
+ // Camera-to-face distance, cached by calcPixelArea; read by the
+ // streaming math's distance signal.
+ F32 mDistanceToCamera = 0.f;
F32 mLastUpdateTime;
F32 mLastSkinTime;
F32 mLastMoveTime;
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index 64359b6cbe..fe77d585c8 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -57,6 +57,8 @@ namespace
LLColor4U MAX_WATER_COLOR(0, 48, 96, 240);
S32 LLSurface::sTextureSize = 256;
+F32 LLSurface::sNearestVisiblePatchDistance = FLT_MAX;
+U32 LLSurface::sNearestVisiblePatchFrame = 0;
// ---------------- LLSurface:: Public Members ---------------
@@ -122,7 +124,7 @@ LLSurface::~LLSurface()
else if (poolp->mReferences.empty())
{
gPipeline.removePool(poolp);
- // Don't enable this until we blitz the draw pool for it as well. -- djs
+ // Don't enable this until we blitz the draw pool for it as well. - djs
mSTexturep = nullptr;
}
else
@@ -583,6 +585,13 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
LLSurfacePatch *patchp;
+ // Reset the cross-region accumulator at the start of each frame.
+ if (sNearestVisiblePatchFrame != gFrameCount)
+ {
+ sNearestVisiblePatchDistance = FLT_MAX;
+ sNearestVisiblePatchFrame = gFrameCount;
+ }
+
mVisiblePatchCount = 0;
for (S32 i=0; i<mNumberOfPatches; i++)
{
@@ -593,6 +602,7 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
{
mVisiblePatchCount++;
patchp->updateCameraDistanceRegion(pos_region);
+ sNearestVisiblePatchDistance = llmin(sNearestVisiblePatchDistance, patchp->getDistance());
}
}
}
@@ -961,7 +971,7 @@ std::ostream& operator<<(std::ostream &s, const LLSurface &S)
void LLSurface::createPatchData()
{
// Assumes mGridsPerEdge, mGridsPerPatchEdge, and mPatchesPerEdge have been properly set
- // TODO -- check for create() called when surface is not empty
+ // TODO - check for create() called when surface is not empty
S32 i, j;
LLSurfacePatch *patchp;
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
index a599019ca5..4bdb90b102 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -163,6 +163,12 @@ public:
F32 mDetailTextureScale; // Number of times to repeat detail texture across this surface
+ // Closest visible patch distance across all surfaces this frame
+ // (meters), or FLT_MAX if none visible. Drives BOOST_TERRAIN
+ // streaming - terrain has no faces registered with its texture.
+ static F32 sNearestVisiblePatchDistance;
+ static U32 sNearestVisiblePatchFrame;
+
private:
void createSTexture();
void initTextures();
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
index 51ade60827..574c200eb4 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -542,6 +542,7 @@ private:
S32 mRequestedDiscard;
S32 mLoadedDiscard;
S32 mDecodedDiscard;
+ S32 mCodecLevels = 0;
LLFrameTimer mRequestedDeltaTimer;
LLFrameTimer mFetchDeltaTimer;
LLTimer mCacheReadTimer;
@@ -1843,6 +1844,10 @@ bool LLTextureFetchWorker::doWork(S32 param)
else
{
llassert_always(mRawImage.notNull());
+ if (mFormattedImage.notNull())
+ {
+ mCodecLevels = (S32)mFormattedImage->getLevels();
+ }
LL_DEBUGS(LOG_TXT) << mID << ": Decoded. Discard: " << mDecodedDiscard
<< " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL;
setState(WRITE_TO_CACHE);
@@ -2774,10 +2779,12 @@ LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id)
// Threads: T*
bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, S32& worker_state,
LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux,
- LLCore::HttpStatus& last_http_get_status)
+ LLCore::HttpStatus& last_http_get_status,
+ S32& codec_levels)
{
LL_PROFILE_ZONE_SCOPED;
bool res = false;
+ codec_levels = 0;
LLTextureFetchWorker* worker = getWorker(id);
if (worker)
{
@@ -2809,6 +2816,9 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, S3
discard_level = worker->mDecodedDiscard;
raw = worker->mRawImage;
aux = worker->mAuxImage;
+ // Cached on the worker so the value survives mFormattedImage
+ // clears (cache-retry, decode-abort, write-to-cache complete).
+ codec_levels = worker->mCodecLevels;
decode_time = worker->mDecodeTime;
fetch_time = worker->mFetchTime;
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
index 851d6c11a0..d75e16ab7c 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -106,7 +106,8 @@ public:
// keep in mind that if fetcher isn't done, it still might need original raw image
bool getRequestFinished(const LLUUID& id, S32& discard_level, S32& worker_state,
LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux,
- LLCore::HttpStatus& last_http_get_status);
+ LLCore::HttpStatus& last_http_get_status,
+ S32& codec_levels);
// Threads: T*
bool updateRequestPriority(const LLUUID& id, F32 priority);
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index d4d07f24ea..3a6e4e2e2d 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -114,32 +114,37 @@ static bool handleRenderAvatarMouselookChanged(const LLSD& newvalue)
static bool handleRenderTextureQualityChanged(const LLSD& newvalue)
{
- // 0=Low, 1=Medium, 2=High, 3=Ultra. Drives max-resolution and per-channel
- // streaming aggressiveness. Channel order is X=normals, Y=diffuse,
- // Z=specular/metallic, W=emissive (matches TextureChannelPriority).
+ // 0=Low, 1=Medium, 2=High, 3=Ultra. Drives max-resolution and the
+ // per-channel TextureChannelPriority + TextureDistanceDiscardPower
+ // exponents. Channel order: X=normals, Y=diffuse, Z=spec, W=emissive.
U32 quality = (U32)newvalue.asInteger();
U32 max_res = 2048;
- LLVector4 channel_priority(5.f, 7.5f, 20.f, 7.5f);
+ LLVector4 channel_priority(1.f, 0.75f, 0.5f, 0.75f);
+ F32 distance_power = 0.5f;
switch (quality)
{
case 0: // Low
max_res = 1024;
- channel_priority.setVec(20.f, 30.f, 80.f, 30.f);
+ channel_priority.setVec(0.5f, 0.75f, 0.1f, 0.5f);
+ distance_power = 0.15f;
break;
case 1: // Medium
- channel_priority.setVec(10.f, 15.f, 40.f, 15.f);
+ channel_priority.setVec(0.75f, 0.75f, 0.3f, 0.75f);
+ distance_power = 0.25f;
break;
case 2: // High
- // defaults above
+ // channel defaults above (1, 0.75, 0.5, 0.75)
+ distance_power = 0.35f;
break;
case 3: // Ultra
default:
- if (quality > 3) quality = 3;
channel_priority.setVec(1.f, 1.f, 1.f, 1.f);
+ distance_power = 0.5f;
break;
}
gSavedSettings.setU32("RenderMaxTextureResolution", max_res);
gSavedSettings.setVector4("TextureChannelPriority", channel_priority);
+ gSavedSettings.setF32("TextureDistanceDiscardPower", distance_power);
return true;
}
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 0f23596c9a..79439b4d58 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -87,6 +87,8 @@ S32 LLViewerTexture::sRawCount = 0;
S32 LLViewerTexture::sAuxCount = 0;
LLFrameTimer LLViewerTexture::sEvaluationTimer;
F32 LLViewerTexture::sDesiredDiscardBias = 0.f;
+F32 LLViewerTexture::sBackgroundFactor = 0.f;
+F32 LLViewerTexture::sMemoryPressureFactor = 0.f;
U32 LLViewerTexture::sBiasTexturesUpdated = 0;
S32 LLViewerTexture::sMaxSculptRez = 128; //max sculpt image size
@@ -97,12 +99,12 @@ constexpr S32 DEFAULT_ICON_DIMENSIONS = 32;
constexpr S32 DEFAULT_THUMBNAIL_DIMENSIONS = 256;
U32 LLViewerTexture::sMinLargeImageSize = 65536; //256 * 256.
U32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA;
-bool LLViewerTexture::sFreezeImageUpdates = false;
F32 LLViewerTexture::sCurrentTime = 0.0f;
constexpr F32 MEMORY_CHECK_WAIT_TIME = 1.0f;
constexpr F32 MIN_VRAM_BUDGET = 768.f;
F32 LLViewerTexture::sFreeVRAMMegabytes = MIN_VRAM_BUDGET;
+F32 LLViewerTexture::sWindowPixelArea = 1.f;
LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
@@ -120,7 +122,7 @@ LLLoadedCallbackEntry::LLLoadedCallbackEntry(loaded_callback_func cb,
LLViewerFetchedTexture* target,
bool pause)
: mCallback(cb),
- mLastUsedDiscard(MAX_DISCARD_LEVEL+1),
+ mLastUsedDiscard(S32_MAX),
mDesiredDiscard(discard_level),
mNeedsImageRaw(need_imageraw),
mUserData(userdata),
@@ -485,6 +487,13 @@ void LLViewerTexture::updateClass()
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
sCurrentTime = gFrameTimeSeconds;
+ if (gViewerWindow)
+ {
+ F32 w = (F32)gViewerWindow->getWindowWidthRaw();
+ F32 h = (F32)gViewerWindow->getWindowHeightRaw();
+ sWindowPixelArea = llmax(w * h, 1.f);
+ }
+
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
@@ -523,6 +532,28 @@ void LLViewerTexture::updateClass()
F32 over_pct = (used - target) / target;
+ // VRAM-pressure feedback loop with progressive backoff. Ramp starts at
+ // backoff_start x target, not at the budget cliff. Proportional in both
+ // directions: ramp = (over-1)(1-factor), decay = factor; converges at
+ // factor = 1 - 1/over_at_backoff_target.
+ {
+ static LLCachedControl<F32> backoff_start(gSavedSettings, "TextureMemoryPressureBackoffStart", 0.85f);
+ F32 backoff_target = target * llclamp((F32)backoff_start, 0.05f, 1.f);
+ F32 over = used / llmax(backoff_target, 1.f);
+ static LLCachedControl<F32> pressure_ramp_rate(gSavedSettings, "TextureMemoryPressureRampRate", 3.0f);
+ F32 dt = gFrameIntervalSeconds;
+ if (over > 1.f)
+ {
+ sMemoryPressureFactor +=
+ (over - 1.f) * (1.f - sMemoryPressureFactor) * (F32)pressure_ramp_rate * dt;
+ }
+ else
+ {
+ sMemoryPressureFactor -= sMemoryPressureFactor * (F32)pressure_ramp_rate * dt;
+ }
+ sMemoryPressureFactor = llclampf(sMemoryPressureFactor);
+ }
+
bool is_sys_low = isSystemMemoryLow();
bool is_low = is_sys_low || over_pct > 0.f;
@@ -569,6 +600,10 @@ void LLViewerTexture::updateClass()
// don't execute above until the slam to 1.5 has a chance to take effect
sEvaluationTimer.reset();
+ // Don't decay bias while downscale is still draining - those bytes
+ // are about to free and the loop would oscillate.
+ bool eviction_in_flight = !gTextureList.mDownScaleQueue.empty();
+
// lower discard bias over time when at least 10% of budget is free
constexpr F32 FREE_PERCENTAGE_TRESHOLD = -0.1f;
constexpr U32 FREE_SYS_MEM_TRESHOLD = 100;
@@ -576,7 +611,8 @@ void LLViewerTexture::updateClass()
const S32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory() + FREE_SYS_MEM_TRESHOLD);
if (sDesiredDiscardBias > 1.f
&& over_pct < FREE_PERCENTAGE_TRESHOLD
- && getFreeSystemMemory() > MIN_FREE_MAIN_MEMORY)
+ && getFreeSystemMemory() > MIN_FREE_MAIN_MEMORY
+ && !eviction_in_flight)
{
static LLCachedControl<F32> high_mem_discard_decrement(gSavedSettings, "RenderHighMemMinDiscardDecrement", .1f);
@@ -622,6 +658,33 @@ void LLViewerTexture::updateClass()
}
}
+ // Background-window ramp: 0 -> 1 at rate per second while backgrounded,
+ // snaps to 0 in foreground. Default 0.011 ~ 90s to saturate.
+ {
+ static LLCachedControl<F32> bg_factor_rate(gSavedSettings, "TextureBackgroundFactorRatePerSec", 0.011f);
+ if (in_background)
+ {
+ sBackgroundFactor += (F32)bg_factor_rate * gFrameIntervalSeconds;
+ sBackgroundFactor = llclampf(sBackgroundFactor);
+ }
+ else
+ {
+ sBackgroundFactor = 0.f;
+ }
+ }
+
+ // Fetch-queue depth as a one-way bias floor (decay path still drops
+ // bias when the queue drains). Pushes bias up before VRAM overflows
+ // during teleport/scene-change floods.
+ if (LLTextureFetch* fetcher = LLAppViewer::getTextureFetch())
+ {
+ S32 pending = fetcher->getNumRequests();
+ static LLCachedControl<F32> fetch_pressure_scale(gSavedSettings, "TextureFetchPressureScale", 1000.f);
+ F32 scale = llmax((F32)fetch_pressure_scale, 1.f);
+ F32 fetch_pressure = llclamp((F32)pending / scale, 0.f, 3.f);
+ sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.f + fetch_pressure);
+ }
+
sDesiredDiscardBias = llclamp(sDesiredDiscardBias, 1.f, 4.f);
if (last_texture_update_count_bias < sDesiredDiscardBias)
{
@@ -638,8 +701,6 @@ void LLViewerTexture::updateClass()
// a problem.
last_texture_update_count_bias = sDesiredDiscardBias;
}
-
- LLViewerTexture::sFreezeImageUpdates = false;
}
//static
@@ -1156,8 +1217,10 @@ void LLViewerFetchedTexture::init(bool firstinit)
mRequestedDownloadPriority = 0.f;
mFullyLoaded = false;
mCanUseHTTP = true;
- mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
- mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
+ mDesiredDiscardLevel = S8_MAX;
+ // S8_MAX = no cap. setMinDiscardLevel takes min(current, new), so
+ // explicit caps from terrain / avatar self / thumbnails still apply.
+ mMinDesiredDiscardLevel = S8_MAX;
mDecodingAux = false;
@@ -1977,21 +2040,10 @@ bool LLViewerFetchedTexture::processFetchResults(S32& desired_discard, S32 curre
setIsMissingAsset();
desired_discard = -1;
}
- else
- {
- //LL_WARNS() << mID << ": Setting min discard to " << current_discard << LL_ENDL;
- if (current_discard >= 0)
- {
- mMinDiscardLevel = current_discard;
- //desired_discard = current_discard;
- }
- else
- {
- S32 dis_level = getDiscardLevel();
- mMinDiscardLevel = dis_level;
- //desired_discard = dis_level;
- }
- }
+ // Transient failure (decoder OOM, network blip): don't latch
+ // mMinDiscardLevel - that would block all future fetches via
+ // the make_request gate. Permanent failures are caught above
+ // (getDiscardLevel()<0 -> setIsMissingAsset).
destroyRawImage();
}
else if (mRawImage.notNull())
@@ -2072,8 +2124,18 @@ bool LLViewerFetchedTexture::updateFetch()
if (mRawImage.notNull()) sRawCount--;
if (mAuxRawImage.notNull()) sAuxCount--;
// keep in mind that fetcher still might need raw image, don't modify original
+ S32 codec_levels = 0;
bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mFetchState, mRawImage, mAuxRawImage,
- mLastHttpGetStatus);
+ mLastHttpGetStatus, codec_levels);
+ if (codec_levels > 0)
+ {
+ mCodecMaxDiscardLevel = (S8)llmin(codec_levels, (S32)S8_MAX);
+ if (codec_levels > 5)
+ {
+ LL_DEBUGS("TextureStream") << "Texture " << mID << " codec-reported max discard "
+ << codec_levels << " (above the historical hardcoded cap of 5)" << LL_ENDL;
+ }
+ }
if (mRawImage.notNull()) sRawCount++;
if (mAuxRawImage.notNull())
{
@@ -2108,7 +2170,9 @@ bool LLViewerFetchedTexture::updateFetch()
}
}
- desired_discard = llmin(desired_discard, getMaxDiscardLevel());
+ // Clamp the fetch request to what the codestream encodes; deeper
+ // discards are served from the GL mip pyramid via scaleDown.
+ desired_discard = llmin(desired_discard, (S32)mCodecMaxDiscardLevel);
bool make_request = true;
if (decode_priority <= 0)
@@ -2116,9 +2180,14 @@ bool LLViewerFetchedTexture::updateFetch()
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - priority <= 0");
make_request = false;
}
- else if (mDesiredDiscardLevel > getMaxDiscardLevel())
+ else if (mDesiredDiscardLevel > (S32)mCodecMaxDiscardLevel &&
+ current_discard >= 0 &&
+ current_discard <= (S32)mCodecMaxDiscardLevel)
{
- LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > max");
+ // scaleDown can serve this from the GL pyramid. (If current is
+ // already past codec_max, fall through so a zoom-in can rebuild —
+ // scaleDown only goes deeper.)
+ LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > codec max");
make_request = false;
}
else if (mNeedsCreateTexture || mIsMissingAsset)
@@ -2570,20 +2639,21 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
S32 gl_discard = getDiscardLevel();
- // If we don't have a legit GL image, set it to be lower than the worst discard level
+ // S32_MAX is the "no data" sentinel; real discards can now exceed
+ // MAX_DISCARD_LEVEL via dimDerivedMaxDiscard.
if (gl_discard == -1)
{
- gl_discard = MAX_DISCARD_LEVEL + 1;
+ gl_discard = S32_MAX;
}
//
// Determine the quality levels of textures that we can provide to callbacks
// and whether we need to do decompression/readback to get it
//
- S32 current_raw_discard = MAX_DISCARD_LEVEL + 1; // We can always do a readback to get a raw discard
+ S32 current_raw_discard = S32_MAX; // We can always do a readback to get a raw discard
S32 best_raw_discard = gl_discard; // Current GL quality level
- S32 current_aux_discard = MAX_DISCARD_LEVEL + 1;
- S32 best_aux_discard = MAX_DISCARD_LEVEL + 1;
+ S32 current_aux_discard = S32_MAX;
+ S32 best_aux_discard = S32_MAX;
LLImageRaw *current_raw_image = nullptr;
if (mIsRawImageValid)
@@ -2683,7 +2753,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
//
// Run raw/auxiliary data callbacks
//
- if (run_raw_callbacks && current_raw_image != nullptr && (current_raw_discard <= getMaxDiscardLevel()))
+ if (run_raw_callbacks && current_raw_image != nullptr && current_raw_discard != S32_MAX)
{
// Do callbacks which require raw image data.
//LL_INFOS() << "doLoadedCallbacks raw for " << getID() << LL_ENDL;
@@ -2723,7 +2793,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
//
// Run GL callbacks
//
- if (run_gl_callbacks && (gl_discard <= getMaxDiscardLevel()))
+ if (run_gl_callbacks && gl_discard != S32_MAX)
{
//LL_INFOS() << "doLoadedCallbacks GL for " << getID() << LL_ENDL;
@@ -3026,11 +3096,6 @@ S8 LLViewerLODTexture::getType() const
return LLViewerTexture::LOD_TEXTURE;
}
-bool LLViewerLODTexture::isUpdateFrozen()
-{
- return LLViewerTexture::sFreezeImageUpdates;
-}
-
// This is gauranteed to get called periodically for every texture
//virtual
void LLViewerLODTexture::processTextureStats()
@@ -3056,18 +3121,15 @@ void LLViewerLODTexture::processTextureStats()
{
mDesiredDiscardLevel = 0;
}
- // Generate the request priority and render priority
- else if (mDontDiscard || !mUseMipMaps)
+ // HUD/UI/preview and mDontDiscard textures bypass streaming - no
+ // face_distance signal applies, they need native resolution.
+ else if (mBoostLevel >= LLGLTexture::BOOST_HIGH
+ || mDontDiscard
+ || !mUseMipMaps)
{
mDesiredDiscardLevel = 0;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
- mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096
- }
- else if (mBoostLevel < LLGLTexture::BOOST_HIGH && mMaxVirtualSize <= 10.f)
- {
- // If the image has not been significantly visible in a while, we don't want it
- mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1));
- mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel);
+ mDesiredDiscardLevel = 1; // 4096^2 source can't be loaded full res
}
else if (!mFullWidth || !mFullHeight)
{
@@ -3076,28 +3138,86 @@ void LLViewerLODTexture::processTextureStats()
}
else
{
- //static const F64 log_2 = log(2.0);
- static const F64 log_4 = log(4.0);
-
F32 discard_level = 0.f;
- // If we know the output width and height, we can force the discard
- // level to the correct value, and thus not decode more texture
- // data than we need to.
+ // floor(log2(max(w, h))) - both the multiplier on the normalized
+ // factor and the cap clamp at the bottom of this function.
+ S32 dim_max_for_image_i = (mFullWidth > 0 && mFullHeight > 0)
+ ? LLImageGL::dimDerivedMaxDiscard(mFullWidth, mFullHeight)
+ : (S32)mCodecMaxDiscardLevel;
+ F32 dim_max_for_image = (F32)dim_max_for_image_i;
+
if (mKnownDrawWidth && mKnownDrawHeight)
{
+ // UI-pinned target dimensions - use pixel-area math.
+ static const F64 log_4 = log(4.0);
S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight;
draw_texels = llclamp(draw_texels, MIN_IMAGE_AREA, MAX_IMAGE_AREA);
-
- // Use log_4 because we're in square-pixel space, so an image
- // with twice the width and twice the height will have mTexelsPerImage
- // 4 * draw_size
discard_level = (F32)(log(mTexelsPerImage / draw_texels) / log_4);
}
else
{
- // Calculate the required scale factor of the image using pixels per texel
- discard_level = (F32)(log(mTexelsPerImage / mMaxVirtualSize) / log_4);
+ // Two 0..1 signals composed multiplicatively:
+ // discard = distance_factor * size_factor * max_discard
+ // distance_factor: face_dist / draw_dist, shaped by
+ // TextureDistanceDiscardPower (default 0.5 = sqrt).
+ // size_factor: 1 - (mMaxOnScreenSize / window_pixels), shaped
+ // by TextureSizeDiscardPower.
+ // Either factor near 0 keeps the result fine - both have to
+ // be high for the texture to go deep.
+ static LLCachedControl<F32> distance_power(gSavedSettings, "TextureDistanceDiscardPower", 0.5f);
+ F32 power = llmax((F32)distance_power, 0.0001f);
+ F32 distance_factor = (power == 1.f) ? mMinDistanceFactor : powf(mMinDistanceFactor, power);
+
+ static LLCachedControl<F32> size_power(gSavedSettings, "TextureSizeDiscardPower", 1.f);
+ F32 sz_power = llmax((F32)size_power, 0.0001f);
+ F32 coverage = llclampf(mMaxOnScreenSize / sWindowPixelArea);
+ F32 inv_cov = 1.f - coverage;
+ F32 size_factor = (sz_power == 1.f) ? inv_cov : powf(inv_cov, sz_power);
+
+ F32 combined = distance_factor * size_factor;
+
+ // Per-channel exponent. 1.0 = baseline; <1.0 pushes combined
+ // toward 1 (max attenuation) faster. Edges are preserved:
+ // pow(0, p) = 0, pow(1, p) = 1.
+ S32 priority_channel = (mPriorityChannel >= 0 && mPriorityChannel < 4) ? (S32)mPriorityChannel : 1;
+ static LLCachedControl<LLVector4> channel_priority(gSavedSettings, "TextureChannelPriority",
+ LLVector4(1.f, 1.f, 1.f, 1.f));
+ F32 channel_power = llmax(channel_priority().mV[priority_channel], 0.0001f);
+ if (channel_power != 1.f)
+ {
+ combined = powf(combined, channel_power);
+ }
+
+ // Own-avatar boost: shave combined for rigged/animated faces
+ // on gAgentAvatarp. Preference, not exemption - applied
+ // before the staleness/background/pressure floors so heavy
+ // pressure can still evict.
+ if (mOnAgentAvatar)
+ {
+ static LLCachedControl<F32> agent_avatar_boost(gSavedSettings, "TextureAgentAvatarBoost", 0.5f);
+ combined *= llclampf((F32)agent_avatar_boost);
+ }
+
+ // Staleness / background floors. Avatar bakes exempt from
+ // background to avoid the universal-cloud bug when re-foregrounding.
+ combined = llmax(combined, mStalenessFactor);
+ if (!isAgentAvatarBoost(mBoostLevel))
+ {
+ combined = llmax(combined, sBackgroundFactor);
+ }
+
+ // VRAM pressure: pow(combined, 1 - factor) bends the curve
+ // without flattening it - pow(0, p) = 0 so close textures
+ // (combined ~ 0) stay near 0 while mid/far push toward 1.
+ // Avatar bakes exempt.
+ if (!isAgentAvatarBoost(mBoostLevel) && sMemoryPressureFactor > 0.f)
+ {
+ F32 pressure_exp = llmax(1.f - sMemoryPressureFactor, 0.0001f);
+ combined = powf(combined, pressure_exp);
+ }
+
+ discard_level = combined * dim_max_for_image;
}
discard_level = floorf(discard_level);
@@ -3106,12 +3226,29 @@ void LLViewerLODTexture::processTextureStats()
if (mFullWidth > max_tex_res || mFullHeight > max_tex_res)
min_discard = 1.f;
- discard_level = llclamp(discard_level, min_discard, (F32)MAX_DISCARD_LEVEL);
+ // dim_max_for_image_i is the per-texture cap. TextureMaxDiscardOverride
+ // raises it (debug). Codec_max applies only to fetches, not here.
+ static LLCachedControl<S32> max_discard_override(gSavedSettings, "TextureMaxDiscardOverride", 0);
+ S32 effective_cap = (max_discard_override > 0) ? (S32)max_discard_override : dim_max_for_image_i;
+ discard_level = llclamp(discard_level, min_discard, (F32)effective_cap);
+
+ mDesiredDiscardLevel = llmin(effective_cap, (S32)discard_level);
+
+ // Apply the setMinDiscardLevel cap, relaxed proportionally under
+ // VRAM pressure - at factor=1 the cap reaches dim_max so capped
+ // textures (terrain, etc.) participate fully in eviction. Caps of
+ // 0 (thumbnails) and avatar bakes are preserved.
+ S32 effective_min_cap = mMinDesiredDiscardLevel;
+ if (sMemoryPressureFactor > 0.f &&
+ mMinDesiredDiscardLevel > 0 && mMinDesiredDiscardLevel < S8_MAX &&
+ !isAgentAvatarBoost(mBoostLevel))
+ {
+ F32 room = (F32)dim_max_for_image_i - (F32)mMinDesiredDiscardLevel;
+ effective_min_cap += (S32)(sMemoryPressureFactor * room);
+ effective_min_cap = llmin(effective_min_cap, dim_max_for_image_i);
+ }
+ mDesiredDiscardLevel = llmin((S8)effective_min_cap, mDesiredDiscardLevel);
- // Can't go higher than the max discard level
- mDesiredDiscardLevel = llmin(getMaxDiscardLevel() + 1, (S32)discard_level);
- // Clamp to min desired discard
- mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, mDesiredDiscardLevel);
//
// At this point we've calculated the quality level that we want,
@@ -3120,7 +3257,9 @@ void LLViewerLODTexture::processTextureStats()
//
S32 current_discard = getDiscardLevel();
- if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED)
+ // Avatar bakes exempt: shrinking mid-bake can leave the avatar
+ // stuck as a cloud until the next bake completes.
+ if (!isAgentAvatarBoost(mBoostLevel))
{
if (current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage)
{ // should scale down
@@ -3128,13 +3267,6 @@ void LLViewerLODTexture::processTextureStats()
}
}
- if (isUpdateFrozen() // we are out of memory and nearing max allowed bias
- && mBoostLevel < LLGLTexture::BOOST_SCULPTED
- && mDesiredDiscardLevel < current_discard)
- {
- // stop requesting more
- mDesiredDiscardLevel = current_discard;
- }
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel);
}
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 2937651995..f4770e5fac 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -203,6 +203,26 @@ protected:
mutable S32 mMaxVirtualSizeResetInterval;
LLFrameTimer mLastReferencedTimer;
+ // Index into TextureChannelPriority Vector4 (X=normals, Y=diffuse,
+ // Z=spec, W=emissive). -1 -> fall back to diffuse.
+ S8 mPriorityChannel = -1;
+
+ // Bind-staleness floor, 0..1. Per-interval increment is 1/max_discard
+ // so any texture saturates after interval x max_discard seconds idle.
+ F32 mStalenessFactor = 0.f;
+
+ // Closest face's face_distance / draw_distance, clamped 0..1.
+ // Defaults to 1 so never-measured textures resolve to deepest discard.
+ F32 mMinDistanceFactor = 1.f;
+
+ // Largest per-face screen-space coverage in pixels. Raw - no bias or
+ // channel-priority contamination.
+ F32 mMaxOnScreenSize = 0.f;
+
+ // Any face on the agent's avatar (rigged / animated). Drives the
+ // own-avatar quality boost in processTextureStats.
+ bool mOnAgentAvatar = false;
+
ll_face_list_t mFaceList[LLRender::NUM_TEXTURE_CHANNELS]; //reverse pointer pointing to the faces using this image as texture
U32 mNumFaces[LLRender::NUM_TEXTURE_CHANNELS];
LLFrameTimer mLastFaceListUpdateTimer ;
@@ -224,16 +244,27 @@ public:
static S32 sAuxCount;
static LLFrameTimer sEvaluationTimer;
static F32 sDesiredDiscardBias;
+ // Backgrounded-window discard floor, 0..1. Ramps while backgrounded,
+ // snaps to 0 in foreground. Avatar bakes exempt.
+ static F32 sBackgroundFactor;
+
+ // VRAM-pressure factor, 0..1. Applied in processTextureStats as
+ // combined = pow(combined, 1 - factor) - bends the curve without
+ // flattening the distance gradient.
+ static F32 sMemoryPressureFactor;
static U32 sBiasTexturesUpdated;
static S32 sMaxSculptRez ;
static U32 sMinLargeImageSize ;
static U32 sMaxSmallImageSize ;
- static bool sFreezeImageUpdates;
static F32 sCurrentTime ;
// estimated free memory for textures, by bias calculation
static F32 sFreeVRAMMegabytes;
+ // Viewport pixel area, refreshed once per frame. Hoisted to keep the
+ // per-texture hot path out of gViewerWindow.
+ static F32 sWindowPixelArea;
+
enum EDebugTexels
{
DEBUG_TEXELS_OFF,
@@ -279,6 +310,16 @@ public:
LLViewerFetchedTexture(const LLImageRaw* raw, FTType f_type, bool usemipmaps);
LLViewerFetchedTexture(const std::string& url, FTType f_type, const LLUUID& id, bool usemipmaps = true);
+ // Avatar bake/skin textures - exempt from non-visibility-driven discard
+ // (staleness, background, pressure) to avoid the universal-cloud bug.
+ static bool isAgentAvatarBoost(S32 boost_level)
+ {
+ return boost_level == BOOST_AVATAR
+ || boost_level == BOOST_AVATAR_BAKED
+ || boost_level == BOOST_AVATAR_SELF
+ || boost_level == BOOST_AVATAR_BAKED_SELF;
+ }
+
public:
struct Compare
@@ -461,6 +502,12 @@ protected:
S8 mDesiredDiscardLevel; // The discard level we'd LIKE to have - if we have it and there's space
S8 mMinDesiredDiscardLevel; // The minimum discard level we'd like to have
+ // Fetch-side discard cap from the J2C codestream's DWT level count
+ // (populated by the fetcher). Distinct from LLImageGL::mMaxDiscardLevel -
+ // scaleDown can still trim the GL pyramid past this.
+ static constexpr S8 sFallbackCodecMaxDiscardLevel = 5; // MIN_DECOMPOSITION_LEVELS
+ S8 mCodecMaxDiscardLevel = sFallbackCodecMaxDiscardLevel;
+
bool mNeedsAux; // We need to decode the auxiliary channels
bool mHasAux; // We have aux channels
bool mDecodingAux; // Are we decoding high components
@@ -540,7 +587,6 @@ public:
S8 getType() const override;
// Process image stats to determine priority/quality requirements.
void processTextureStats() override;
- bool isUpdateFrozen() ;
bool scaleDown() override;
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index 7dd32074cf..235888e2d1 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -47,6 +47,7 @@
#include "message.h"
#include "lldrawpoolbump.h" // to init bumpmap images
+#include "llagentcamera.h"
#include "lltexturecache.h"
#include "lltexturefetch.h"
#include "llviewercontrol.h"
@@ -61,6 +62,8 @@
#include "lltracerecording.h"
#include "llviewerdisplay.h"
#include "llviewerwindow.h"
+#include "llsurface.h"
+#include "llvoavatarself.h"
#include "llprogressview.h"
////////////////////////////////////////////////////////////////////////////
@@ -93,6 +96,20 @@ LLTextureKey::LLTextureKey(LLUUID id, ETexListType tex_type)
///////////////////////////////////////////////////////////////////////////////
+// eTexIndex -> TextureChannelPriority component index (X=normals, Y=diffuse,
+// Z=spec, W=emissive). Single source of truth - route all channel-priority
+// lookups through this table.
+const S32 LLViewerTextureList::sChannelToPriority[LLRender::NUM_TEXTURE_CHANNELS] =
+{
+ 1, // DIFFUSE_MAP (0) -> Y (diffuse)
+ 0, // NORMAL_MAP / ALT_DIFFUSE (1) -> X (normals)
+ 2, // SPECULAR_MAP (2) -> Z (specular/metallic)
+ 1, // BASECOLOR_MAP (3) -> Y (diffuse)
+ 2, // METALLIC_ROUGHNESS_MAP (4) -> Z (specular/metallic)
+ 0, // GLTF_NORMAL_MAP (5) -> X (normals)
+ 3, // EMISSIVE_MAP (6) -> W (emissive)
+};
+
LLViewerTextureList::LLViewerTextureList()
: mForceResetTextureStats(false),
mInitialized(false)
@@ -899,8 +916,7 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
{
llassert(!gCubeSnapshot);
- constexpr F32 BIAS_TRS_OUT_OF_SCREEN = 1.5f;
- constexpr F32 BIAS_TRS_ON_SCREEN = 1.f;
+ constexpr F32 BIAS_TRS_ON_SCREEN = 1.f; // perf gate for face-loop early exit
if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH) // don't bother checking face list for boosted textures
{
@@ -910,9 +926,35 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
F32 max_vsize = 0.f;
bool on_screen = false;
+ // Accumulators for the per-texture signals published below.
+ // Defaults map to "deepest discard wanted" until evidence updates them.
+ F32 min_distance_factor = 1.f;
+ F32 max_on_screen_size = 0.f;
+ bool on_agent_avatar = false;
+ F32 draw_distance = llmax(gAgentCamera.mDrawDistance, 0.001f);
+
U32 face_count = 0;
U32 max_faces_to_check = 1024;
+ // Pick the least-aggressive channel across all uses, so a texture
+ // used as both diffuse and normal isn't penalized by its harshest
+ // role. -1 sentinel keeps emissive-only textures (W=3) from being
+ // clobbered by a smaller init value.
+ S32 priority_channel = -1;
+ for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+ {
+ if (imagep->getNumFaces(i) > 0)
+ {
+ S32 mapped = sChannelToPriority[i];
+ priority_channel = (priority_channel < 0) ? mapped : llmin(priority_channel, mapped);
+ }
+ }
+ if (priority_channel < 0)
+ {
+ priority_channel = 1; // no faces - default to diffuse
+ }
+ imagep->mPriorityChannel = (S8)priority_channel;
+
// get adjusted bias based on image resolution
LLImageGL* img = imagep->getGLTexture();
F32 max_discard = F32(img ? img->getMaxDiscardLevel() : MAX_DISCARD_LEVEL);
@@ -948,6 +990,14 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
on_screen |= face->mInFrustum;
+ F32 dist_factor = llclampf(face->mDistanceToCamera / draw_distance);
+ min_distance_factor = llmin(min_distance_factor, dist_factor);
+
+ if (face->mAvatar && face->mAvatar == gAgentAvatarp)
+ {
+ on_agent_avatar = true;
+ }
+
// Scale desired texture resolution higher or lower depending on texture scale
//
// Minimum usage examples: a 1024x1024 texture with aplhabet (texture atlas),
@@ -963,6 +1013,10 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max());
vsize /= min_scale;
+ // Raw screen-space coverage - taken before the bias /
+ // camera-boost mutations below so the size signal is clean.
+ max_on_screen_size = llmax(max_on_screen_size, vsize);
+
// apply bias to offscreen faces all the time, but only to onscreen faces when bias is large
// use mImportanceToCamera to make bias switch a bit more gradual
if (!face->mInFrustum || LLViewerTexture::sDesiredDiscardBias > 1.9f + face->mImportanceToCamera / 2.f)
@@ -970,13 +1024,6 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
vsize /= bias;
}
- // boost resolution of textures that are important to the camera
- if (face->mInFrustum)
- {
- static LLCachedControl<F32> texture_camera_boost(gSavedSettings, "TextureCameraBoost", 8.f);
- vsize *= llmax(face->mImportanceToCamera*texture_camera_boost, 1.f);
- }
-
max_vsize = llmax(max_vsize, vsize);
// addTextureStats limits size to sMaxVirtualSize
@@ -995,57 +1042,89 @@ void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imag
}
}
- if (face_count > max_faces_to_check)
+ bool used_face_fast_path = (face_count > max_faces_to_check);
+ if (used_face_fast_path)
{ // this texture is used in so many places we should just boost it and not bother checking its vsize
// this is especially important because the above is not time sliced and can hit multiple ms for a single texture
max_vsize = MAX_IMAGE_AREA;
}
- if (imagep->getType() == LLViewerTexture::LOD_TEXTURE && imagep->getBoostLevel() == LLViewerTexture::BOOST_NONE)
- { // conditionally reset max virtual size for unboosted LOD_TEXTURES
- // this is an alternative to decaying mMaxVirtualSize over time
- // that keeps textures from continously downrezzing and uprezzing in the background
+ imagep->addTextureStats(max_vsize);
- if (LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_OUT_OF_SCREEN ||
- (!on_screen && LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_ON_SCREEN))
- {
- imagep->mMaxVirtualSize = 0.f;
- }
+ // Publish per-texture signals for processTextureStats. Closest face
+ // wins for distance (min); biggest face wins for size (max). Default
+ // distance=1, size=0 maps to "deepest discard wanted" - never-
+ // measured textures stay coarse until distance/size evidence arrives.
+ if (used_face_fast_path)
+ {
+ // Fast path saw only a prefix of faces - force best-quality
+ // sentinels to match the MAX_IMAGE_AREA vsize boost above.
+ imagep->mMinDistanceFactor = 0.f;
+ imagep->mMaxOnScreenSize = (F32)MAX_IMAGE_AREA;
}
+ else if (face_count == 0 && imagep->getBoostLevel() == LLGLTexture::BOOST_TERRAIN)
+ {
+ // Terrain detail textures don't register faces with the texture
+ // (LLVOSurfacePatch addFace(NULL)). Drive distance from the LOD
+ // system; floor at a small nonzero value so pressure has
+ // something to bite into (pow(0, p) = 0).
+ static LLCachedControl<F32> terrain_distance_floor(gSavedSettings, "TextureTerrainDistanceFloor", 0.01f);
+ static LLCachedControl<F32> terrain_coverage(gSavedSettings, "TextureTerrainCoverageFraction", 0.99f);
+ F32 nearest = LLSurface::sNearestVisiblePatchDistance;
+ F32 dist = (nearest < FLT_MAX) ? llclampf(nearest / draw_distance) : 1.f;
+ imagep->mMinDistanceFactor = llmax(dist, llclampf((F32)terrain_distance_floor));
+ imagep->mMaxOnScreenSize = LLViewerTexture::sWindowPixelArea * llclampf((F32)terrain_coverage);
+ }
+ else
+ {
+ imagep->mMinDistanceFactor = min_distance_factor;
+ imagep->mMaxOnScreenSize = max_on_screen_size;
+ }
+ imagep->mOnAgentAvatar = on_agent_avatar;
- imagep->addTextureStats(max_vsize);
-
- // Derive stream priority channel from face lists.
- // Map render texture channels to priority channels:
- // 0 = normal, 1 = diffuse, 2 = specular, 3 = emissive
+ // Bind-staleness. Avatar bakes exempt (cloud-bug protection).
+ // Per-interval increment is 1/max_discard so saturation time is
+ // interval * max_discard seconds regardless of texture size.
+ // Never-bound textures defer to distance/size or initial fetch
+ // could never start.
+ if (LLViewerFetchedTexture::isAgentAvatarBoost(imagep->getBoostLevel()))
+ {
+ imagep->mStalenessFactor = 0.f;
+ }
+ else if (LLImageGL* gli = imagep->getGLTexture())
{
- static const S32 render_to_priority[] = {
- 1, // DIFFUSE_MAP (0)
- 0, // NORMAL_MAP / ALTERNATE_DIFFUSE_MAP (1)
- 2, // SPECULAR_MAP (2)
- 1, // BASECOLOR_MAP (3)
- 2, // METALLIC_ROUGHNESS_MAP (4)
- 0, // GLTF_NORMAL_MAP (5)
- 3, // EMISSIVE_MAP (6)
- };
+ static LLCachedControl<F32> bind_decay_seconds(gSavedSettings, "TextureBindDecaySeconds", 5.f);
+ static LLCachedControl<F32> staleness_interval(gSavedSettings, "TextureStalenessIntervalSeconds", 5.f);
+ F32 grace = llmax((F32)bind_decay_seconds, 0.f);
+ F32 interval = llmax((F32)staleness_interval, 0.0001f);
- S32 priority_channel = 1; // default to diffuse
- for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+ bool ever_bound = (gli->mLastBindTime > 0.f);
+ F32 time_since_bind = ever_bound ? (LLImageGL::sLastFrameTime - gli->mLastBindTime) : 0.f;
+
+ if (!ever_bound || time_since_bind <= grace)
{
- if (imagep->getNumFaces(i) > 0)
- {
- priority_channel = llmin(priority_channel, render_to_priority[i]);
- }
+ imagep->mStalenessFactor = 0.f;
}
-
- static LLCachedControl<LLVector4> channel_priority(gSavedSettings, "TextureChannelPriority",
- LLVector4(10.0f, 20.0f, 40.0f, 20.0f));
- F32 factor = llmax(channel_priority().mV[priority_channel], 0.1f);
- if (factor != 1.0f)
+ else
{
- imagep->mMaxVirtualSize /= factor;
+ S32 full_w = imagep->getFullWidth();
+ S32 full_h = imagep->getFullHeight();
+ S32 max_discard = (full_w > 0 && full_h > 0)
+ ? LLImageGL::dimDerivedMaxDiscard(full_w, full_h)
+ : (S32)gli->getMaxDiscardLevel();
+ if (max_discard > 0)
+ {
+ F32 steps = (time_since_bind - grace) / interval;
+ F32 step_size = 1.f / (F32)max_discard;
+ imagep->mStalenessFactor = llclampf(steps * step_size);
+ }
+ else
+ {
+ imagep->mStalenessFactor = 0.f;
+ }
}
}
+
}
#if 0
@@ -1154,8 +1233,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
imagep->postCreateTexture();
imagep->mCreatePending = false;
- if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel() &&
- (imagep->getDesiredDiscardLevel() <= MAX_DISCARD_LEVEL))
+ if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel())
{
// NOTE: this may happen if the desired discard reduces while a decode is in progress and does not
// necessarily indicate a problem, but if log occurrences excede that of dsiplay_stats: FPS,
@@ -1180,7 +1258,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
gCopyProgram.bind();
gPipeline.mScreenTriangleVB->setBuffer();
- // give time to downscaling first -- if mDownScaleQueue is not empty, we're running out of memory and need
+ // give time to downscaling first - if mDownScaleQueue is not empty, we're running out of memory and need
// to free up memory by discarding off screen textures quickly
// do at least 5 and make sure we don't get too far behind even if it violates
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 7c7112f4cf..dd8655cd6f 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -30,6 +30,7 @@
#include "lluuid.h"
//#include "message.h"
#include "llgl.h"
+#include "llrender.h"
#include "llviewertexture.h"
#include "llui.h"
#include <list>
@@ -92,6 +93,10 @@ class LLViewerTextureList
friend class LLLocalBitmap;
public:
+ // eTexIndex -> TextureChannelPriority component (X=normals, Y=diffuse,
+ // Z=spec, W=emissive). Single source of truth.
+ static const S32 sChannelToPriority[LLRender::NUM_TEXTURE_CHANNELS];
+
static bool createUploadFile(LLPointer<LLImageRaw> raw_image,
const std::string& out_filename,
const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index 3441e25c6a..eb0261b5e5 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -173,7 +173,10 @@ LLPointer<LLViewerFetchedTexture> fetch_terrain_texture(const LLUUID& id)
return nullptr;
}
- LLPointer<LLViewerFetchedTexture> tex = LLViewerTextureManager::getFetchedTexture(id);
+ // LOD_TEXTURE so streaming math runs (the base-class processTextureStats
+ // pins mDesiredDiscardLevel at 0).
+ LLPointer<LLViewerFetchedTexture> tex = LLViewerTextureManager::getFetchedTexture(
+ id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
return tex;
}
@@ -343,18 +346,9 @@ bool LLTerrainMaterials::makeTextureReady(LLPointer<LLViewerFetchedTexture>& tex
{
if (boost)
{
+ // Quality is driven by the streaming math via synthetic signals
+ // for BOOST_TERRAIN textures in updateImageDecodePriority.
boost_minimap_texture(tex, BASE_SIZE*BASE_SIZE);
-
- S32 width = tex->getFullWidth();
- S32 height = tex->getFullHeight();
- S32 min_dim = llmin(width, height);
- S32 ddiscard = 0;
- while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
- {
- ddiscard++;
- min_dim /= 2;
- }
- tex->setMinDiscardLevel(ddiscard);
}
return false;
}