summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@lindenlab.com>2026-07-23 08:19:10 -0400
committerGitHub <noreply@github.com>2026-07-23 08:19:10 -0400
commitbcfda3b68a95bbbfb8b53f6313929fc5aeaa591c (patch)
tree5982185abc453619dddf6f234e1bc6802e1d69b8
parent8f2d07b717db2997c5701977ef3d65a56c811771 (diff)
parentf364b5c882da959284e8fc1b3bc80810a7374969 (diff)
Merge pull request #6029 from secondlife/geenz/26.3-texture-streaming-revert
Remove texture streaming changes from 26.3
-rw-r--r--indra/llimage/llimagej2c.cpp34
-rw-r--r--indra/llimage/llimagej2c.h5
-rw-r--r--indra/llimagej2coj/llimagej2coj.cpp29
-rw-r--r--indra/llimagej2coj/llimagej2coj.h19
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp30
-rw-r--r--indra/llkdu/llimagej2ckdu.h2
-rw-r--r--indra/llkdu/tests/llimagej2ckdu_test.cpp2
-rw-r--r--indra/llprimitive/lltextureentry.cpp2
-rw-r--r--indra/llrender/llgl.cpp6
-rw-r--r--indra/llrender/llgl.h1
-rw-r--r--indra/llrender/llimagegl.cpp162
-rw-r--r--indra/llrender/llimagegl.h46
-rw-r--r--indra/llrender/llrender.cpp21
-rw-r--r--indra/llxml/llcontrol.cpp45
-rw-r--r--indra/llxml/llcontrol.h8
-rw-r--r--indra/newview/app_settings/settings.xml289
-rw-r--r--indra/newview/featuretable.txt32
-rw-r--r--indra/newview/featuretable_linux.txt2
-rw-r--r--indra/newview/featuretable_mac.txt32
-rw-r--r--indra/newview/llface.cpp52
-rw-r--r--indra/newview/llface.h25
-rw-r--r--indra/newview/llfeaturemanager.cpp24
-rw-r--r--indra/newview/llfetchedgltfmaterial.cpp29
-rw-r--r--indra/newview/llfloatersettingsdebug.cpp42
-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/lltextureview.cpp9
-rw-r--r--indra/newview/llviewercontrol.cpp59
-rw-r--r--indra/newview/llviewerdisplay.cpp31
-rw-r--r--indra/newview/llviewerobject.cpp18
-rw-r--r--indra/newview/llviewershadermgr.cpp6
-rw-r--r--indra/newview/llviewertexture.cpp595
-rw-r--r--indra/newview/llviewertexture.h104
-rw-r--r--indra/newview/llviewertexturelist.cpp513
-rw-r--r--indra/newview/llviewertexturelist.h10
-rw-r--r--indra/newview/llviewerwindow.cpp8
-rw-r--r--indra/newview/llvlcomposition.cpp18
-rw-r--r--indra/newview/llvovolume.cpp63
-rw-r--r--indra/newview/pipeline.cpp6
-rw-r--r--indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml32
42 files changed, 603 insertions, 1843 deletions
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 8099a18045..5a941dc958 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -268,11 +268,35 @@ S32 LLImageJ2C::calcHeaderSizeJ2C()
//static
S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
{
- // Dispatch to the linked impl so OpenJPEG (block-aligned, needs
- // over-allocation) and KDU (packet-aligned, lean) each return what
- // their decoder actually needs.
- static std::unique_ptr<LLImageJ2CImpl> s_estimator(fallbackCreateLLImageJ2CImpl());
- return s_estimator->estimateDataSize(w, h, comp, discard_level, rate);
+ // Note: This provides an estimation for the first to last quality layer of a given discard level
+ // This is however an efficient approximation, as the true discard level boundary would be
+ // in general too big for fast fetching.
+ // For details about the equation used here, see https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
+
+ // Estimate the number of layers. This is consistent with what's done for j2c encoding in LLImageJ2CKDU::encodeImpl().
+ constexpr S32 precision = 8; // assumed bitrate per component channel, might change in future for HDR support
+ constexpr S32 max_components = 4; // assumed the file has four components; three color and alpha
+ // Use MAX_IMAGE_SIZE_DEFAULT (currently 2048) if either dimension is unknown (zero)
+ S32 width = (w > 0) ? w : 2048;
+ S32 height = (h > 0) ? h : 2048;
+ S32 max_dimension = llmax(width, height); // Find largest dimension
+ S32 block_area = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE; // Calculated initial block area from established max block size (currently 64)
+ S32 max_layers = (S32)llmax(llround(log2f((float)max_dimension) - log2f((float)MAX_BLOCK_SIZE)), 4); // Find number of powers of two between extents and block size to a minimum of 4
+ block_area *= llmax(max_layers, 1); // Adjust initial block area by max number of layers
+ S32 totalbytes = (S32) (MIN_LAYER_SIZE * max_components * precision); // Start estimation with a minimum reasonable size
+ S32 block_layers = 0;
+ while (block_layers <= max_layers) // Walk the layers
+ {
+ if (block_layers <= (5 - discard_level)) // Walk backwards from discard 5 to required discard layer.
+ totalbytes += (S32) (block_area * max_components * precision * rate); // Add each block layer reduced by assumed compression rate
+ block_layers++; // Move to next layer
+ block_area *= 4; // Increase block area by power of four
+ }
+
+ totalbytes /= 8; // to bytes
+ totalbytes += calcHeaderSizeJ2C(); // header
+
+ return totalbytes;
}
S32 LLImageJ2C::calcHeaderSize()
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
index 81b24cc0b3..19744a7f87 100644
--- a/indra/llimage/llimagej2c.h
+++ b/indra/llimage/llimagej2c.h
@@ -106,11 +106,6 @@ class LLImageJ2CImpl
{
public:
virtual ~LLImageJ2CImpl();
-
- // Estimate the byte size of a J2C codestream sufficient to decode the
- // given discard level. KDU uses a packet-by-packet impl; OpenJPEG
- // overrides with a more conservative block-aligned estimate.
- virtual S32 estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const = 0;
protected:
// Find out the image size and number of channels.
// Return value:
diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp
index 488c07e1c9..7cfadb889d 100644
--- a/indra/llimagej2coj/llimagej2coj.cpp
+++ b/indra/llimagej2coj/llimagej2coj.cpp
@@ -919,32 +919,3 @@ bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
base.setSize(width, height, components);
return true;
}
-
-
-// OpenJPEG-tuned byte estimator. Conservative pyramid walk that accounts for
-// OJ's whole-code-block decode behavior (even with strict mode off). Larger
-// images get a per-resolution multiplier so the byte range lands inside the
-// last needed code-block boundary.
-S32 LLImageJ2COJ::estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const
-{
- constexpr S32 precision = 8;
- constexpr S32 max_components = 4;
- S32 width = (w > 0) ? w : 2048;
- S32 height = (h > 0) ? h : 2048;
- S32 max_dimension = llmax(width, height);
- S32 block_area = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE;
- S32 max_layers = (S32)llmax(llround(log2f((float)max_dimension) - log2f((float)MAX_BLOCK_SIZE)), 4);
- block_area *= llmax(max_layers, 1);
- S32 totalbytes = (S32)(MIN_LAYER_SIZE * max_components * precision);
- S32 block_layers = 0;
- while (block_layers <= max_layers)
- {
- if (block_layers <= (5 - discard_level))
- totalbytes += (S32)(block_area * max_components * precision * rate);
- block_layers++;
- block_area *= 4;
- }
- totalbytes /= 8;
- totalbytes += LLImageJ2C::calcHeaderSizeJ2C();
- return totalbytes;
-}
diff --git a/indra/llimagej2coj/llimagej2coj.h b/indra/llimagej2coj/llimagej2coj.h
index 0cc8d5c34c..da49597302 100644
--- a/indra/llimagej2coj/llimagej2coj.h
+++ b/indra/llimagej2coj/llimagej2coj.h
@@ -35,20 +35,15 @@ class LLImageJ2COJ : public LLImageJ2CImpl
{
public:
LLImageJ2COJ();
- virtual ~LLImageJ2COJ() override;
+ virtual ~LLImageJ2COJ();
protected:
- virtual bool getMetadata(LLImageJ2C &base) override;
- virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) override;
+ virtual bool getMetadata(LLImageJ2C &base);
+ virtual bool decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count);
virtual bool encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0,
- bool reversible = false) override;
- virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL) override;
- virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0) override;
- virtual std::string getEngineInfo() const override;
-public:
- // OpenJPEG decodes whole code-blocks even with strict mode off, so the
- // lean packet-walk under-allocates and clips quality. Keep the older
- // conservative pyramid-with-multiplier estimate here.
- virtual S32 estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const override;
+ bool reversible = false);
+ virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
+ virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0);
+ virtual std::string getEngineInfo() const;
};
#endif
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 74aaffbd65..cf45f93168 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -1551,33 +1551,3 @@ void kdc_flow_control::process_components()
}
}
}
-
-// Layer-factored byte estimator. Walks the resolution pyramid to count
-// layers, weights by layer_factor, then picks between a sqrt-based "new"
-// estimate and a raw-dimensions "old" estimate per TextureNewByteRange.
-// Reference: https://wiki.lindenlab.com/wiki/THX1138_KDU_Improvements#Byte_Range_Study
-S32 LLImageJ2CKDU::estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const
-{
- S32 width = (w > 0) ? w : 2048;
- S32 height = (h > 0) ? h : 2048;
- S32 nb_layers = 1;
- S32 surface = width * height;
- S32 s = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE;
- while (surface > s)
- {
- nb_layers++;
- s *= 4;
- }
- F32 layer_factor = 3.0f * (7 - llclamp(nb_layers, 1, 6));
-
- width >>= discard_level;
- height >>= discard_level;
- width = llmax(width, 1);
- height = llmax(height, 1);
-
- S32 new_bytes = (S32)(sqrtf((F32)(width * height)) * (F32)comp * rate * 1000.f / layer_factor);
- S32 old_bytes = (S32)((F32)(width * height * comp) * rate);
- S32 bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes)) ? new_bytes : old_bytes;
- bytes = llmax(bytes, LLImageJ2C::calcHeaderSizeJ2C());
- return bytes;
-}
diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h
index 6079585948..c9aa0c5250 100644
--- a/indra/llkdu/llimagej2ckdu.h
+++ b/indra/llkdu/llimagej2ckdu.h
@@ -67,8 +67,6 @@ protected:
virtual bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, int discard_level = -1, int* region = NULL);
virtual bool initEncode(LLImageJ2C &base, LLImageRaw &raw_image, int blocks_size = -1, int precincts_size = -1, int levels = 0);
virtual std::string getEngineInfo() const;
-public:
- virtual S32 estimateDataSize(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate) const;
private:
bool initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, ECodeStreamMode mode, S32 first_channel, S32 max_channel_count, int discard_level = -1, int* region = NULL);
diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp
index 6be2d3a078..bc52a15c4a 100644
--- a/indra/llkdu/tests/llimagej2ckdu_test.cpp
+++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp
@@ -83,8 +83,6 @@ void LLImageBase::setSize(S32 , S32 , S32 ) { }
bool LLImageBase::isBufferInvalid() const { return false; }
LLImageJ2CImpl::~LLImageJ2CImpl() { }
-bool LLImage::sUseNewByteRange = false;
-S32 LLImageJ2C::calcHeaderSizeJ2C() { return 0; }
LLImageFormatted::LLImageFormatted(S8 ) { }
LLImageFormatted::~LLImageFormatted() { }
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
index 2b0f989701..ac482ffbf9 100644
--- a/indra/llprimitive/lltextureentry.cpp
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -600,7 +600,7 @@ LLGLTFMaterial* LLTextureEntry::getGLTFRenderMaterial() const
return mGLTFRenderMaterial;
}
- //llassert(getGLTFMaterialOverride() == nullptr || getGLTFMaterialOverride()->isClearedForBaseMaterial());
+ llassert(getGLTFMaterialOverride() == nullptr || getGLTFMaterialOverride()->isClearedForBaseMaterial());
return getGLTFMaterial();
}
diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp
index 0e59c449db..4584ed1d86 100644
--- a/indra/llrender/llgl.cpp
+++ b/indra/llrender/llgl.cpp
@@ -2370,12 +2370,6 @@ void clear_glerror()
glGetError();
}
-void drain_glerror()
-{
- // bounded: a lost/reset context can return errors indefinitely
- for (S32 i = 0; i < 16 && glGetError() != GL_NO_ERROR; ++i) {}
-}
-
///////////////////////////////////////////////////////////////
//
// LLGLState
diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h
index 3f9a9de70a..e1ab2a49e6 100644
--- a/indra/llrender/llgl.h
+++ b/indra/llrender/llgl.h
@@ -159,7 +159,6 @@ void log_glerror();
void assert_glerror();
void clear_glerror();
-void drain_glerror(); // pops ALL pending GL error flags (bounded so a lost/reset context that returns errors forever cannot hang the caller); use before an attributable glGetError check.
# define stop_glerror() assert_glerror()
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index b3cd8d2896..5292ef7f6f 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;
-// Per-mip upload paths call this once per level; only free_tex_image
-// removes a texture's accounting entirely.
+// track a texture alloc on the currently bound texture.
+// asserts that no currently tracked alloc exists
void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 intformat, U32 count)
{
U32 texUnit = gGL.getCurrentTexUnitIndex();
@@ -80,43 +80,12 @@ void LLImageGLMemory::alloc_tex_image(U32 width, U32 height, U32 intformat, U32
sTexMemMutex.lock();
- 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);
- }
+ // it is a precondition that no existing allocation exists for this texture
+ llassert(sTextureAllocs.find(texName) == sTextureAllocs.end());
- U32 texUnit = gGL.getCurrentTexUnitIndex();
- U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture();
+ sTextureAllocs[texName] = size;
+ sTextureBytes += size;
- sTexMemMutex.lock();
- auto iter = sTextureAllocs.find(texName);
- if (iter != sTextureAllocs.end())
- {
- iter->second += extra;
- sTextureBytes += extra;
- }
sTexMemMutex.unlock();
}
@@ -166,8 +135,6 @@ U64 LLImageGL::getTextureBytesAllocated()
//statics
U32 LLImageGL::sUniqueCount = 0;
-std::atomic<U32> LLImageGL::sOOMErrorCount(0);
-thread_local bool LLImageGL::sStampBindFrame = true;
U32 LLImageGL::sBindCount = 0;
S32 LLImageGL::sCount = 0;
@@ -493,7 +460,7 @@ bool LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, b
//----------------------------------------------------------------------------
LLImageGL::LLImageGL(bool usemipmaps/* = true*/, bool allow_compression/* = true*/)
-: mExternalTexture(false)
+: mSaveData(0), mExternalTexture(false)
{
init(usemipmaps, allow_compression);
setSize(0, 0, 0);
@@ -502,7 +469,7 @@ LLImageGL::LLImageGL(bool usemipmaps/* = true*/, bool allow_compression/* = true
}
LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps/* = true*/, bool allow_compression/* = true*/)
-: mExternalTexture(false)
+: mSaveData(0), mExternalTexture(false)
{
llassert( components <= 4 );
init(usemipmaps, allow_compression);
@@ -512,7 +479,7 @@ LLImageGL::LLImageGL(U32 width, U32 height, U8 components, bool usemipmaps/* = t
}
LLImageGL::LLImageGL(const LLImageRaw* imageraw, bool usemipmaps/* = true*/, bool allow_compression/* = true*/)
-: mExternalTexture(false)
+: mSaveData(0), mExternalTexture(false)
{
init(usemipmaps, allow_compression);
setSize(0, 0, 0);
@@ -621,6 +588,8 @@ void LLImageGL::cleanup()
destroyGLTexture();
}
freePickMask();
+
+ mSaveData = NULL; // deletes data
}
//----------------------------------------------------------------------------
@@ -715,10 +684,7 @@ void LLImageGL::dump()
//----------------------------------------------------------------------------
void LLImageGL::forceUpdateBindStats(void) const
{
- // Intentionally a no-op: mLastBindTime is written only by real bind
- // paths so the staleness signal reflects actual GPU use. Callers that
- // still invoke this (avatar "keep alive" sites, deleted-texture
- // fallback) no longer falsely refresh staleness.
+ mLastBindTime = sLastFrameTime;
}
bool LLImageGL::updateBindStats() const
@@ -777,7 +743,6 @@ void LLImageGL::setImage(const LLImageRaw* imageraw)
bool LLImageGL::setImage(const U8* data_in, bool data_hasmips /* = false */, S32 usename /* = 0 */)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- LLImageGLStampBypass no_stamp; // upload binds are not visibility
const bool is_compressed = isCompressed();
@@ -873,7 +838,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)
{
@@ -899,7 +864,6 @@ 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();
}
@@ -1497,18 +1461,7 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
LL_PROFILE_ZONE_NUM(width);
LL_PROFILE_ZONE_NUM(height);
- // 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();
- }
-
- // Drain stale GL errors so an OOM detected below belongs to this alloc.
- // Otherwise a failed glTexImage2D is swallowed in release while
- // alloc_tex_image still counts the bytes, inflating the used-VRAM figure.
- drain_glerror();
-
+ free_cur_tex_image();
const bool use_sub_image = should_stagger_image_set(compress);
if (!use_sub_image)
{
@@ -1518,30 +1471,19 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt
else
{
// break up calls to a manageable size for the GL command buffer
- LL_PROFILE_ZONE_NAMED("glTexImage2D alloc");
- glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, nullptr);
- }
+ {
+ LL_PROFILE_ZONE_NAMED("glTexImage2D alloc");
+ glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, nullptr);
+ }
- if (glGetError() == GL_OUT_OF_MEMORY)
- {
- ++sOOMErrorCount;
- LL_WARNS_ONCE("Texture") << "glTexImage2D failed with GL_OUT_OF_MEMORY ("
- << width << "x" << height << " mip " << miplevel
- << ") - not counting bytes" << LL_ENDL;
- }
- else
- {
- if (use_sub_image)
+ U8* src = (U8*)(pixels);
+ if (src)
{
- U8* src = (U8*)(pixels);
- if (src)
- {
- LL_PROFILE_ZONE_NAMED("glTexImage2D copy");
- sub_image_lines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width);
- }
+ LL_PROFILE_ZONE_NAMED("glTexImage2D copy");
+ sub_image_lines(target, miplevel, 0, 0, width, height, pixformat, pixtype, src, width);
}
- alloc_tex_image(width, height, intformat, 1);
}
+ alloc_tex_image(width, height, intformat, 1);
}
stop_glerror();
}
@@ -1671,6 +1613,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
{
destroyGLTexture();
mCurrentDiscardLevel = discard_level;
+ mLastBindTime = sLastFrameTime;
mGLTextureCreated = false;
return true ;
}
@@ -1686,7 +1629,6 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
LL_PROFILE_GPU_ZONE("createGLTexture");
checkActiveThread();
- LLImageGLStampBypass no_stamp; // creation binds are not visibility
bool main_thread = on_main_thread();
@@ -1787,7 +1729,9 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_
mTextureMemory = (S64Bytes)getMipBytes(mCurrentDiscardLevel);
- mGLCreateTime = sLastFrameTime;
+
+ // mark this as bound at this point, so we don't throw it out immediately
+ mLastBindTime = sLastFrameTime;
checkActiveThread();
return true;
@@ -1919,8 +1863,7 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
//-----------------------------------------------------------------------------------------------
GLenum error ;
- S32 error_count = 0 ;
- while((error = glGetError()) != GL_NO_ERROR && ++error_count <= 16)
+ while((error = glGetError()) != GL_NO_ERROR)
{
LL_WARNS() << "GL Error happens before reading back texture. Error code: " << error << LL_ENDL ;
}
@@ -1980,8 +1923,7 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
imageraw->deleteData() ;
- error_count = 0 ;
- while((error = glGetError()) != GL_NO_ERROR && ++error_count <= 16)
+ while((error = glGetError()) != GL_NO_ERROR)
{
LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ;
}
@@ -2097,37 +2039,6 @@ 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
-{
- // Both stamps skip same-frame re-binds (bindFast runs per draw). They dedupe
- // separately, so a non-camera pass touching the time stamp first doesn't stop
- // a real camera bind from setting the frame stamp later the same frame.
- if (mLastBindTime != sLastFrameTime)
- {
- mLastBindTime = sLastFrameTime;
- }
- if (sStampBindFrame)
- {
- const U32 frame = LLFrameTimer::getFrameCount();
- if (mLastBindFrame != frame)
- {
- mLastBindFrame = frame;
- }
- }
-}
-
S64 LLImageGL::getBytes(S32 discard_level) const
{
if (discard_level < 0)
@@ -2550,12 +2461,6 @@ bool LLImageGL::scaleDown(S32 desired_discard)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- // Don't let eviction re-arm the GC: the glGenerateMipmap re-bind below would
- // otherwise stamp mLastBindFrame, so the next computeDesiredDiscard treats the
- // just-evicted texture as freshly drawn, un-floors it, and re-fetches - the
- // evict/refetch oscillation.
- LLImageGLStampBypass no_stamp;
-
if (mTarget != GL_TEXTURE_2D
|| mFormatInternal == -1 // not initialized
)
@@ -2563,12 +2468,7 @@ bool LLImageGL::scaleDown(S32 desired_discard)
return false;
}
- // 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);
+ desired_discard = llmin(desired_discard, mMaxDiscardLevel);
if (desired_discard <= mCurrentDiscardLevel)
{
@@ -2601,7 +2501,6 @@ 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);
}
}
@@ -2647,7 +2546,6 @@ 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 ca75b543c0..6b4492c09e 100644
--- a/indra/llrender/llimagegl.h
+++ b/indra/llrender/llimagegl.h
@@ -39,7 +39,6 @@
#include "llrender.h"
#include "threadpool.h"
#include "workqueue.h"
-#include <atomic>
#include <unordered_set>
#define LL_IMAGEGL_THREAD_CHECK 0 //set to 1 to enable thread debugging for ImageGL
@@ -52,11 +51,6 @@ 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();
@@ -157,15 +151,6 @@ 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; }
@@ -239,23 +224,14 @@ public:
public:
// Various GL/Rendering options
S64Bytes mTextureMemory;
- mutable F32 mLastBindTime = 0.f; // wall-clock time at last stampBound (bind or bind-attempt)
- mutable U32 mLastBindFrame = 0; // frame index (LLFrameTimer::getFrameCount) at last CAMERA-pass
- // stampBound; 0 = never. Drives visibility GC + fetch gating.
- F32 mGLCreateTime = 0.f; // wall-clock time the GL texture was created
-
- // When false, stampBound skips the mLastBindFrame stamp (mLastBindTime still
- // updates). Set false around non-camera passes (probes, shadows, impostors)
- // and administrative binds (upload, scaleDown - via LLImageGLStampBypass) so
- // those binds don't count as camera visibility. Thread-local so a GL upload
- // thread can't flip it on the render thread mid-frame.
- static thread_local bool sStampBindFrame;
+ mutable F32 mLastBindTime; // last time this was bound, by discard level
private:
U32 createPickMask(S32 pWidth, S32 pHeight);
void freePickMask();
bool isCompressed();
+ LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL
LL::WorkQueue::weak_t mMainQueue;
U8* mPickMask; //downsampled bitmap approximation of alpha channel. NULL if no alpha channel
U16 mPickMaskWidth;
@@ -309,11 +285,6 @@ public:
// Global memory statistics
static U32 sBindCount; // Tracks number of texture binds for current frame
static U32 sUniqueCount; // Tracks number of unique texture binds for current frame
- // glTexImage2D GL_OUT_OF_MEMORY failures detected (bytes NOT counted for
- // these). Written from whichever thread runs texture creation; read by
- // the streaming 1Hz pressure log. Nonzero = the driver is refusing
- // allocations and the VRAM budget is unreliable.
- static std::atomic<U32> sOOMErrorCount;
static bool sGlobalUseAnisotropic;
static LLImageGL* sDefaultGLTexture ;
static bool sAutomatedTest;
@@ -369,19 +340,6 @@ public:
};
-// RAII: suppress the mLastBindFrame stamp for the current scope. Use around
-// administrative binds (upload, create, scaleDown) so they don't count as
-// camera visibility - otherwise the GC's own scaleDown re-stamps what it just
-// aged out and oscillates. Saves/restores, so it nests correctly.
-class LLImageGLStampBypass
-{
-public:
- LLImageGLStampBypass() : mPrev(LLImageGL::sStampBindFrame) { LLImageGL::sStampBindFrame = false; }
- ~LLImageGLStampBypass() { LLImageGL::sStampBindFrame = mPrev; }
-private:
- bool mPrev;
-};
-
class LLImageGLThread : public LLSimpleton<LLImageGLThread>, LL::ThreadPool
{
public:
diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp
index f0a1c44507..65525c1449 100644
--- a/indra/llrender/llrender.cpp
+++ b/indra/llrender/llrender.cpp
@@ -198,9 +198,6 @@ void LLTexUnit::bindFast(LLTexture* texture)
gGL.mCurrTextureUnitIndex = mIndex;
mCurrTexture = gl_tex->getTexName();
mCurrTexType = gl_tex->getTarget();
- // 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");
@@ -245,11 +242,6 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
texture->setActive() ;
texture->updateBindStatsForTester() ;
}
- // updateBindStats only stamps time; the GC and fetch gate use
- // the frame stamp, so stamp it here too or bind()-drawn faces
- // (bump/material/media) oscillate. Admin/non-camera binds are
- // already suppressed via LLImageGLStampBypass / sStampBindFrame.
- gl_tex->stampBound();
mHasMipMaps = gl_tex->mHasMipMaps;
if (gl_tex->mTexOptionsDirty)
{
@@ -258,17 +250,11 @@ 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);
@@ -330,8 +316,6 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind, S32
glBindTexture(sGLTextureType[texture->getTarget()], mCurrTexture);
stop_glerror();
texture->updateBindStats();
- // Frame-stamp fresh binds too - see bind(LLTexture*) above.
- texture->stampBound();
mHasMipMaps = texture->mHasMipMaps;
if (texture->mTexOptionsDirty)
{
@@ -342,11 +326,6 @@ 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/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
index 5a37167f18..34643d5f5c 100644
--- a/indra/llxml/llcontrol.cpp
+++ b/indra/llxml/llcontrol.cpp
@@ -37,7 +37,6 @@
#include "llstring.h"
#include "v3math.h"
#include "v3dmath.h"
-#include "v4math.h"
#include "v4coloru.h"
#include "v4color.h"
#include "v3color.h"
@@ -64,7 +63,6 @@ template <> eControlType get_control_type<std::string>();
template <> eControlType get_control_type<LLVector3>();
template <> eControlType get_control_type<LLVector3d>();
-template <> eControlType get_control_type<LLVector4>();
template <> eControlType get_control_type<LLRect>();
template <> eControlType get_control_type<LLColor4>();
template <> eControlType get_control_type<LLColor3>();
@@ -74,7 +72,6 @@ template <> eControlType get_control_type<LLSD>();
template <> LLSD convert_to_llsd<U32>(const U32& in);
template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
-template <> LLSD convert_to_llsd<LLVector4>(const LLVector4& in);
template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in);
@@ -88,7 +85,6 @@ template <> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlT
template <> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name);
template <> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name);
template <> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name);
-template <> LLVector4 convert_from_llsd<LLVector4>(const LLSD& sd, eControlType type, std::string_view control_name);
template <> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name);
template <> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, std::string_view control_name);
template <> LLColor4U convert_from_llsd<LLColor4U>(const LLSD& sd, eControlType type, std::string_view control_name);
@@ -128,9 +124,6 @@ bool LLControlVariable::llsd_compare(const LLSD& a, const LLSD & b)
case TYPE_VEC3D:
result = LLVector3d(a) == LLVector3d(b);
break;
- case TYPE_VEC4:
- result = LLVector4(a) == LLVector4(b);
- break;
case TYPE_QUAT:
result = LLQuaternion(a) == LLQuaternion(b);
break;
@@ -380,7 +373,6 @@ const std::string LLControlGroup::mTypeString[TYPE_COUNT] = { "U32"
,"Rect"
,"Color4"
,"Color3"
- ,"Vector4"
,"LLSD"
};
@@ -540,11 +532,6 @@ LLControlVariable* LLControlGroup::declareVec3d(const std::string& name, const L
return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist);
}
-LLControlVariable* LLControlGroup::declareVec4(const std::string& name, const LLVector4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
-{
- return declareControl(name, TYPE_VEC4, initial_val.getValue(), comment, persist);
-}
-
LLControlVariable* LLControlGroup::declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist)
{
return declareControl(name, TYPE_QUAT, initial_val.getValue(), comment, persist);
@@ -627,11 +614,6 @@ LLVector3d LLControlGroup::getVector3d(std::string_view name)
return get<LLVector3d>(name);
}
-LLVector4 LLControlGroup::getVector4(std::string_view name)
-{
- return get<LLVector4>(name);
-}
-
LLQuaternion LLControlGroup::getQuaternion(std::string_view name)
{
return get<LLQuaternion>(name);
@@ -732,11 +714,6 @@ void LLControlGroup::setVector3d(std::string_view name, const LLVector3d &val)
set(name, val);
}
-void LLControlGroup::setVector4(std::string_view name, const LLVector4 &val)
-{
- set(name, val);
-}
-
void LLControlGroup::setQuaternion(std::string_view name, const LLQuaternion &val)
{
set(name, val);
@@ -1285,11 +1262,6 @@ template <> eControlType get_control_type<LLVector3d>()
return TYPE_VEC3D;
}
-template <> eControlType get_control_type<LLVector4>()
-{
- return TYPE_VEC4;
-}
-
template <> eControlType get_control_type<LLQuaternion>()
{
return TYPE_QUAT;
@@ -1330,11 +1302,6 @@ template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in)
{
return in.getValue();
}
-template <> LLSD convert_to_llsd<LLVector4>(const LLVector4& in)
-{
- return in.getValue();
-}
-
template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in)
{
return in.getValue();
@@ -1452,18 +1419,6 @@ LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std:
}
template<>
-LLVector4 convert_from_llsd<LLVector4>(const LLSD& sd, eControlType type, std::string_view control_name)
-{
- if (type == TYPE_VEC4)
- return LLVector4(sd);
- else
- {
- CONTROL_ERRS << "Invalid LLVector4 value for " << control_name << ": " << LLControlGroup::typeEnumToString(type) << " " << sd << LL_ENDL;
- return LLVector4();
- }
-}
-
-template<>
LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, std::string_view control_name)
{
if (type == TYPE_QUAT)
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h
index c2bcd20c85..5aa2b9715e 100644
--- a/indra/llxml/llcontrol.h
+++ b/indra/llxml/llcontrol.h
@@ -43,7 +43,6 @@
class LLVector3;
class LLVector3d;
-class LLVector4;
class LLQuaternion;
class LLColor4;
class LLColor3;
@@ -62,7 +61,6 @@ typedef enum e_control_type
TYPE_RECT,
TYPE_COL4,
TYPE_COL3,
- TYPE_VEC4,
TYPE_LLSD,
TYPE_COUNT
} eControlType;
@@ -198,7 +196,6 @@ public:
LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
- LLControlVariable* declareVec4(const std::string& name, const LLVector4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
@@ -215,7 +212,6 @@ public:
LLWString getWString(std::string_view name);
LLVector3 getVector3(std::string_view name);
LLVector3d getVector3d(std::string_view name);
- LLVector4 getVector4(std::string_view name);
LLRect getRect(std::string_view name);
LLSD getLLSD(std::string_view name);
LLQuaternion getQuaternion(std::string_view name);
@@ -254,7 +250,6 @@ public:
void setString(std::string_view name, const std::string& val);
void setVector3(std::string_view name, const LLVector3 &val);
void setVector3d(std::string_view name, const LLVector3d &val);
- void setVector4(std::string_view name, const LLVector4 &val);
void setQuaternion(std::string_view name, const LLQuaternion &val);
void setRect(std::string_view name, const LLRect &val);
void setColor4(std::string_view name, const LLColor4 &val);
@@ -421,7 +416,6 @@ template <> eControlType get_control_type<bool>();
template <> eControlType get_control_type<std::string>();
template <> eControlType get_control_type<LLVector3>();
template <> eControlType get_control_type<LLVector3d>();
-template <> eControlType get_control_type<LLVector4>();
template <> eControlType get_control_type<LLQuaternion>();
template <> eControlType get_control_type<LLRect>();
template <> eControlType get_control_type<LLColor4>();
@@ -431,7 +425,6 @@ template <> eControlType get_control_type<LLSD>();
template <> LLSD convert_to_llsd<U32>(const U32& in);
template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
-template <> LLSD convert_to_llsd<LLVector4>(const LLVector4& in);
template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in);
template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
@@ -441,7 +434,6 @@ template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlTy
template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, std::string_view control_name);
template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, std::string_view control_name);
template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, std::string_view control_name);
-template<> LLVector4 convert_from_llsd<LLVector4>(const LLSD& sd, eControlType type, std::string_view control_name);
template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, std::string_view control_name);
template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, std::string_view control_name);
template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, std::string_view control_name);
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1c2c2d0559..d411eeb148 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -8002,6 +8002,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>RenderMinFreeMainMemoryThreshold</key>
+ <map>
+ <key>Comment</key>
+ <string>If available free physical memory is below this value textures get agresively scaled down</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>U32</string>
+ <key>Value</key>
+ <integer>512</integer>
+ </map>
<key>RenderLowMemMinDiscardIncrement</key>
<map>
<key>Comment</key>
@@ -8024,27 +8035,27 @@
<key>Value</key>
<real>0.1</real>
</map>
- <key>RenderMaxTextureResolution</key>
+ <key>RenderMaxTextureIndex</key>
<map>
<key>Comment</key>
- <string>Maximum texture resolution to download for non-boosted textures. Driven by RenderTextureQuality.</string>
+ <string>Maximum texture index to use for indexed texture rendering.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
- <integer>2048</integer>
+ <integer>16</integer>
</map>
- <key>RenderTextureQuality</key>
+ <key>RenderMaxTextureResolution</key>
<map>
<key>Comment</key>
- <string>Texture quality preset: 0=Low, 1=Medium, 2=High, 3=Ultra. Drives RenderMaxTextureResolution, TexturePixelToTexelRatio, TexturePressureTightenRate, TexturePressureRelaxRate, and TextureChannelRatio* (Normal/BaseColor/Specular/Emissive). The pressure water marks (TexturePressureHighWater/LowWater) are constant across tiers.</string>
+ <string>Maximum texture resolution to download for non-boosted textures.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
- <integer>2</integer>
+ <integer>2048</integer>
</map>
<key>RenderDownScaleMethod</key>
<map>
@@ -8057,6 +8068,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>RenderDebugTextureBind</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable texture bind performance test.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>RenderDelayCreation</key>
<map>
<key>Comment</key>
@@ -9232,7 +9254,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>
@@ -9373,6 +9395,17 @@
<key>Value</key>
<integer>64</integer>
</map>
+ <key>RenderReservedTextureIndices</key>
+ <map>
+ <key>Comment</key>
+ <string>Count of texture indices to reserve for shadow and reflection maps when using indexed texture rendering. Probably only want to set from the login screen.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>14</integer>
+ </map>
<key>RenderResolutionDivisor</key>
<map>
<key>Comment</key>
@@ -11827,214 +11860,16 @@
<key>Value</key>
<real>20.0</real>
</map>
- <key>TextureChannelRatioNormal</key>
- <map>
- <key>Comment</key>
- <string>Per-channel pixel:texel ratio multiplier for normal maps (texels per pixel, applied on top of the global TexturePixelToTexelRatio). 1.0 = full quality; lower = coarser. Driven by the RenderTextureQuality preset.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>1.0</real>
- </map>
- <key>TextureChannelRatioBaseColor</key>
- <map>
- <key>Comment</key>
- <string>Per-channel pixel:texel ratio multiplier for base color / diffuse (texels per pixel, applied on top of the global TexturePixelToTexelRatio). 1.0 = full quality. Driven by the RenderTextureQuality preset.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>1.0</real>
- </map>
- <key>TextureChannelRatioSpecular</key>
- <map>
- <key>Comment</key>
- <string>Per-channel pixel:texel ratio multiplier for specular / metallic-roughness (texels per pixel, applied on top of the global TexturePixelToTexelRatio). 0.5 = half resolution (specular detail is usually less perceptible than diffuse). Driven by the RenderTextureQuality preset.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.5</real>
- </map>
- <key>TextureChannelRatioEmissive</key>
- <map>
- <key>Comment</key>
- <string>Per-channel pixel:texel ratio multiplier for emissive (texels per pixel, applied on top of the global TexturePixelToTexelRatio). 0.5 = half resolution. Driven by the RenderTextureQuality preset.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.5</real>
- </map>
- <key>TextureMaxDiscardOverride</key>
+ <key>TextureCameraBoost</key>
<map>
<key>Comment</key>
- <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>
+ <string>Amount to boost resolution of textures that are important to the camera.</string>
<key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>S32</string>
- <key>Value</key>
<integer>0</integer>
- </map>
- <key>TexturePixelToTexelRatio</key>
- <map>
- <key>Comment</key>
- <string>Max texels per screen pixel the streamer will allocate (the "R" in a 1:R pixel:texel ratio). 1.0 = one texel per pixel. VRAM pressure walks the effective ratio down from here.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>1.0</real>
- </map>
- <key>TextureBackgroundMinRatio</key>
- <map>
- <key>Comment</key>
- <string>Lowest pixel:texel ratio the streamer decays to while the viewer is backgrounded, to free VRAM for other apps. Lower frees more but re-rezzes slower on return. Restored when focus comes back.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.001</real>
- </map>
- <key>TexturePressureHighWater</key>
- <map>
- <key>Comment</key>
- <string>High water mark as a fraction of the VRAM budget. When used VRAM crosses this, the global pixel:texel ratio tightens (backs off detail) at TexturePressureTightenRate. Held at 0.90 - it's a physical "crossed the budget" threshold, not a tier preference.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.90</real>
- </map>
- <key>TexturePressureLowWater</key>
- <map>
- <key>Comment</key>
- <string>Low water mark as a fraction of the VRAM budget. When used VRAM drops below this, the global pixel:texel ratio relaxes (restores detail) at TexturePressureRelaxRate. The band between low and high is the hysteresis zone where the ratio holds steady - wide enough to prevent sawtooth.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.70</real>
- </map>
- <key>TexturePressureTightenRate</key>
- <map>
- <key>Comment</key>
- <string>Rate (ratio units/sec) at which the global pixel:texel ratio drops while used VRAM is above the high watermark. Deliberately slow so eviction (scaleDown draining) frees bytes before the next step - prevents over-shoot / thrash.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.30</real>
- </map>
- <key>TexturePressureRelaxRate</key>
- <map>
- <key>Comment</key>
- <string>Rate (ratio units/sec) at which the global pixel:texel ratio climbs back toward TexturePixelToTexelRatio while used VRAM is below the low watermark. Typically slower than the tighten rate so detail returns gradually as headroom appears.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.10</real>
- </map>
- <key>TextureUpRezMargin</key>
- <map>
- <key>Comment</key>
- <string>Hysteresis dead-band (in mip levels) around a texture's current discard. A texture only fetches a finer mip when its ideal discard falls more than this below the current level, and only evicts when it rises more than this above - prevents fetch/scaleDown thrash at mip boundaries when an object slowly recedes.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>0.2</real>
- </map>
- <key>TextureCooldownStepSeconds</key>
- <map>
- <key>Comment</key>
- <string>While backgrounded, the pixel:texel ratio drops one mip toward TextureBackgroundMinRatio every this many seconds.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>5.0</real>
- </map>
- <key>TextureGCStepFrames</key>
- <map>
- <key>Comment</key>
- <string>Foreground GC cooldown, in rendered frames. For every this many frames a texture goes without being drawn, its mip drops by TextureGCStepMips. Resets when the texture is drawn again.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>U32</string>
- <key>Value</key>
- <integer>5</integer>
- </map>
- <key>TextureGCStepMips</key>
- <map>
- <key>Comment</key>
- <string>Mip levels dropped each time a TextureGCStepFrames cooldown elapses. 1 is gentlest; higher sheds VRAM faster in coarser jumps.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>U32</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
- <key>TextureLoadTargetFPS</key>
- <map>
- <key>Comment</key>
- <string>Frame rate the viewer is willing to drop to while loading textures. The texture pipeline's per-frame budget is the headroom between this and the actual frame cost, so fast machines load aggressively and slow ones hold their frame rate.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>30.0</real>
- </map>
- <key>TextureLoadBudgetMaxMS</key>
- <map>
- <key>Comment</key>
- <string>Hard cap, in milliseconds per frame, on the adaptive texture-pipeline budget.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>F32</string>
- <key>Value</key>
- <real>10.0</real>
- </map>
- <key>TextureFetchStepMips</key>
- <map>
- <key>Comment</key>
- <string>Fetch refinement step, in mip levels: a texture whose resident data is coarser than desired by more than this fetches in steps of this size instead of jumping straight to the final resolution, so it sharpens progressively instead of sitting blurry then popping. 0 = jump directly. Boosted/pinned textures always jump.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>U32</string>
- <key>Value</key>
- <integer>2</integer>
- </map>
- <key>TextureFrustumAllowance</key>
- <map>
- <key>Comment</key>
- <string>Falloff width for out-of-frustum texture resolution, as a fraction of screen size. Content grazing the screen edge keeps full resolution; content this far past the edge reaches the deepest mip, lerped between. Keeps barely-out-of-view textures resident so panning back doesn't refetch them.</string>
- <key>Persist</key>
- <integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>0.5</real>
+ <real>8.0</real>
</map>
<key>TextureDecodeDisabled</key>
<map>
@@ -12069,6 +11904,28 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>TextureDiscardBackgroundedTime</key>
+ <map>
+ <key>Comment</key>
+ <string>Specify how long to wait before discarding texture data after viewer is backgrounded. (zero or negative to disable)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>60.0</real>
+ </map>
+ <key>TextureDiscardMinimizedTime</key>
+ <map>
+ <key>Comment</key>
+ <string>Specify how long to wait before discarding texture data after viewer is minimized. (zero or negative to disable)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>1.0</real>
+ </map>
<key>TextureFetchConcurrency</key>
<map>
<key>Comment</key>
@@ -12157,27 +12014,27 @@
<key>Value</key>
<string />
</map>
- <key>TextureAvatarBoost</key>
+ <key>TextureScaleMinAreaFactor</key>
<map>
<key>Comment</key>
- <string>Coverage multiplier for avatar textures: worn attachments (rigged extents make their measurement unreliable) and baked system-avatar textures. 4.0 = one mip finer than measured. A bonus, not a pin - nearby avatars gain headroom while distant avatars still downrez with their measured coverage. 1.0 disables.</string>
+ <string>Limits how texture scale affects area calculation.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>4.0</real>
+ <real>0.0095</real>
</map>
- <key>TextureDownrezCoverageBias</key>
+ <key>TextureScaleMaxAreaFactor</key>
<map>
<key>Comment</key>
- <string>Which end of a texture's texels-per-pixel spread sizes it. Each texture tracks the screen coverage of its most demanding use (lowest texels per pixel) and least demanding use (highest texels per pixel, most oversampled). 0 = size to the most demanding use (best quality); 1 = size to the least demanding use (frees the most memory). Interpolation is geometric (log-space), so the resulting discard level moves linearly with this value - 0.5 sits halfway between the two ends in mip levels. Default 0.25 is the best universal balance.</string>
+ <string>Limits how texture scale affects area calculation.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>0.25</real>
+ <real>25.0</real>
</map>
<key>ThreadPoolSizes</key>
<map>
@@ -16377,7 +16234,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>
@@ -16531,7 +16388,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/featuretable.txt b/indra/newview/featuretable.txt
index 72e0f43c0e..1090dd8ffb 100644
--- a/indra/newview/featuretable.txt
+++ b/indra/newview/featuretable.txt
@@ -1,4 +1,4 @@
-version 76
+version 74
// The version number above should be incremented IF AND ONLY IF some
// change has been made that is sufficiently important to justify
// resetting the graphics preferences of all users to the recommended
@@ -68,6 +68,7 @@ RenderShadowDetail 1 2
RenderUseStreamVBO 1 1
RenderFSAAType 1 2
RenderFSAASamples 1 3
+RenderMaxTextureIndex 1 16
RenderGLContextCoreProfile 1 1
RenderGLMultiThreadedTextures 1 0
RenderGLMultiThreadedMedia 1 1
@@ -84,7 +85,7 @@ RenderExposure 1 4
RenderTonemapType 1 1
RenderTonemapMix 1 1
RenderDisableVintageMode 1 1
-RenderTextureQuality 1 3
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -127,6 +128,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
+RenderMaxTextureResolution 1 512
RenderReflectionProbeCount 1 1
//
@@ -169,6 +171,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
+RenderMaxTextureResolution 1 1024
RenderReflectionProbeCount 1 32
//
@@ -210,6 +213,7 @@ RenderCASSharpness 1 0
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
@@ -251,6 +255,7 @@ RenderCASSharpness 1 0
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
@@ -292,6 +297,7 @@ RenderCASSharpness 1 0.4
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 128
//
@@ -333,6 +339,7 @@ RenderCASSharpness 1 0.4
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -374,6 +381,7 @@ RenderCASSharpness 1 0.4
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -392,24 +400,6 @@ list VRAMGT512
RenderCompressTextures 1 0
//
-// VRAM 4GB to 8GB
-//
-list VRAMLT8GB
-RenderTextureQuality 1 2
-
-//
-// VRAM 2GB to 4GB
-//
-list VRAMLT4GB
-RenderTextureQuality 1 1
-
-//
-// VRAM 2GB and under
-//
-list VRAMLT2GB
-RenderTextureQuality 1 0
-
-//
// "Default" setups for safe, low, medium, high
//
list safe
@@ -425,7 +415,7 @@ RenderShadowDetail 0 0
RenderReflectionProbeDetail 0 -1
RenderMirrors 0 0
RenderDisableVintageMode 1 0
-RenderTextureQuality 1 2
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 0 0
list Intel
diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt
index a39c64510f..d8d4f08429 100644
--- a/indra/newview/featuretable_linux.txt
+++ b/indra/newview/featuretable_linux.txt
@@ -66,6 +66,7 @@ RenderDeferredSSAO 1 1
RenderUseAdvancedAtmospherics 1 0
RenderShadowDetail 1 2
RenderFSAASamples 1 16
+RenderMaxTextureIndex 1 16
RenderMirrors 1 1
//
@@ -490,6 +491,7 @@ RenderVBOEnable 1 0
list OpenGLPre30
RenderDeferred 0 0
+RenderMaxTextureIndex 1 1
list Intel
RenderAnisotropic 1 0
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
index ebfe45d9ea..c3e2dd0c41 100644
--- a/indra/newview/featuretable_mac.txt
+++ b/indra/newview/featuretable_mac.txt
@@ -1,4 +1,4 @@
-version 75
+version 73
// The version number above should be incremented IF AND ONLY IF some
// change has been made that is sufficiently important to justify
// resetting the graphics preferences of all users to the recommended
@@ -65,6 +65,7 @@ RenderShadowDetail 1 2
RenderUseStreamVBO 1 1
RenderFSAAType 1 2
RenderFSAASamples 1 3
+RenderMaxTextureIndex 1 16
RenderGLContextCoreProfile 1 1
RenderGLMultiThreadedTextures 1 1
RenderGLMultiThreadedMedia 1 1
@@ -84,7 +85,7 @@ RenderTonemapType 1 1
RenderTonemapMix 1 1
RenderDisableVintageMode 1 1
RenderDownScaleMethod 1 0
-RenderTextureQuality 1 3
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -127,6 +128,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
+RenderMaxTextureResolution 1 512
RenderReflectionProbeCount 1 1
//
@@ -169,6 +171,7 @@ RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
RenderDisableVintageMode 1 0
+RenderMaxTextureResolution 1 1024
RenderReflectionProbeCount 1 32
//
@@ -210,6 +213,7 @@ RenderCASSharpness 1 0
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
@@ -251,6 +255,7 @@ RenderCASSharpness 1 0
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 64
//
@@ -292,6 +297,7 @@ RenderCASSharpness 1 0
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 128
//
@@ -333,6 +339,7 @@ RenderCASSharpness 1 0.4
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -374,6 +381,7 @@ RenderCASSharpness 1 0.4
RenderExposure 1 1
RenderTonemapType 1 1
RenderTonemapMix 1 0.7
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 1 256
//
@@ -393,24 +401,6 @@ list VRAMGT512
RenderCompressTextures 1 0
//
-// VRAM 4GB to 8GB
-//
-list VRAMLT8GB
-RenderTextureQuality 1 2
-
-//
-// VRAM 2GB to 4GB
-//
-list VRAMLT4GB
-RenderTextureQuality 1 1
-
-//
-// VRAM 2GB and under
-//
-list VRAMLT2GB
-RenderTextureQuality 1 0
-
-//
// "Default" setups for safe, low, medium, high
//
list safe
@@ -424,7 +414,7 @@ RenderDeferredSSAO 0 0
RenderShadowDetail 0 0
RenderMirrors 0 0
RenderDisableVintageMode 1 0
-RenderTextureQuality 1 2
+RenderMaxTextureResolution 1 2048
RenderReflectionProbeCount 0 0
list TexUnit8orLess
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
index c96e1bd3b0..018d4c4bba 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,18 +2265,7 @@ F32 LLFace::getTextureVirtualSize()
face_area = mPixelArea / llclamp(texel_area, 0.015625f, 128.f);
}
- // 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);
+ face_area = LLFace::adjustPixelArea(mImportanceToCamera, face_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())
@@ -2297,8 +2286,6 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
// don't update every frame
if (gFrameTimeSeconds - mLastPixelAreaUpdate < PIXEL_AREA_UPDATE_PERIOD)
{
- cos_angle_to_view_dir = mLastCosAngleToViewDir;
- radius = mLastRadius;
return true;
}
@@ -2370,7 +2357,6 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
// no rigged extents, zero out bounding box and skip update
mRiggedExtents[0] = mRiggedExtents[1] = LLVector4a(0.f, 0.f, 0.f);
- mInFrustum = false;
return false;
}
@@ -2403,7 +2389,6 @@ 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() ;
@@ -2425,7 +2410,6 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
if(!camera->AABBInFrustum(center, size))
{
mImportanceToCamera = 0.f ;
- mInFrustum = false;
return false ;
}
if(cos_angle_to_view_dir > camera->getCosHalfFov()) //the center is within the view frustum
@@ -2454,24 +2438,6 @@ bool LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius)
mImportanceToCamera = LLFace::calcImportanceToCamera(cos_angle_to_view_dir, dist) ;
}
- // On-screen test: does the face's projected disc overlap the screen disc?
- // (Same construction as adjustPartialOverlapPixelArea.) Behind-camera faces
- // get acos(cos) near pi and fall out; the generous screen radius errs toward
- // "on screen" so fetch admission never starves edge content. mFrustumOverflow
- // is how far past the boundary the disc sits, as a fraction of screen size -
- // 0 on screen, 0.1 = 10% of a screen out - and feeds the frustum allowance
- // falloff in computeDesiredDiscard.
- {
- F32 center_px = acosf(llclamp(cos_angle_to_view_dir, -1.f, 1.f)) * LLDrawable::sCurPixelAngle;
- F32 screen_radius = (F32)llmax(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw());
- F32 past_edge = center_px - radius - screen_radius;
- mInFrustum = past_edge <= 5.f;
- mFrustumOverflow = llmax(past_edge - 5.f, 0.f) / screen_radius;
- }
-
- mLastCosAngleToViewDir = cos_angle_to_view_dir;
- mLastRadius = radius;
-
return true ;
}
@@ -2547,16 +2513,8 @@ F32 LLFace::calcImportanceToCamera(F32 cos_angle_to_view_dir, F32 dist)
}
//static
-F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_area, S32 source_area)
+F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_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.
@@ -2564,11 +2522,11 @@ F32 LLFace::adjustPixelArea(F32 importance, F32 pixel_area, S32 source_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 > large_floor) //if is large image, shrink face_area by considering the partial overlapping.
+ else if(pixel_area > LLViewerTexture::sMinLargeImageSize) //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 = large_floor ;
+ pixel_area = (F32)LLViewerTexture::sMinLargeImageSize ;
}
}
}
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
index 0b0a17b8c8..6e9d23c3a2 100644
--- a/indra/newview/llface.h
+++ b/indra/newview/llface.h
@@ -242,9 +242,7 @@ private:
bool calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) ;
public:
static F32 calcImportanceToCamera(F32 to_view_dir, F32 dist);
- // 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) ;
+ static F32 adjustPixelArea(F32 importance, F32 pixel_area) ;
public:
@@ -253,9 +251,6 @@ 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;
@@ -269,21 +264,10 @@ public:
// return mSkinInfo->mHash or 0 if mSkinInfo is null
U64 getSkinHash();
- // True if this face's projected bounding disc overlaps the screen - maintained
- // by calcPixelArea() (sticky between its throttled updates). Drives per-texture
- // fetch admission (LLViewerTextureList::updateImageDecodePriority -> mOnScreen).
+ // true if face was recently in the main camera frustum according to LLViewerTextureList updates
bool mInFrustum = false;
- // How far past the screen boundary the projected disc sits, as a fraction of
- // screen size (0 = on screen). Feeds the frustum-allowance falloff so barely
- // out-of-view content keeps its resolution. Maintained with mInFrustum.
- F32 mFrustumOverflow = 0.f;
// value of gFrameCount the last time the face was touched by LLViewerTextureList::updateImageDecodePriority
U32 mLastTextureUpdate = 0;
- // Cached per-channel streaming coverage (repeat-adjusted screen pixels),
- // refreshed at the mLastTextureUpdate cadence and shared by every texture
- // on this face. 0 = degenerate / not yet measured. See
- // update_face_stream_vsize in llviewertexturelist.cpp.
- F32 mStreamVSize[LLRender::NUM_TEXTURE_CHANNELS] = {};
private:
LLPointer<LLVertexBuffer> mVertexBuffer;
@@ -320,11 +304,6 @@ private:
// gFrameTimeSeconds when mPixelArea was last updated
F32 mLastPixelAreaUpdate = 0.f;
- // Last cos-angle-to-view-dir and projected radius computed by calcPixelArea;
- // reused by its throttled early-return so the overlap test gets real values.
- F32 mLastCosAngleToViewDir = 1.f;
- F32 mLastRadius = 0.f;
-
// virtual size of face in texture area (mPixelArea adjusted by texture repeats)
// used to determine desired resolution of texture
F32 mVSize;
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index aab2865ef7..c8692224f1 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -752,29 +752,9 @@ void LLFeatureManager::applyBaseMasks()
{
maskFeatures("VRAMGT512");
}
-
- // Texture quality is driven by detected VRAM. Feature masks take the MIN
- // of applied values, so cascading lower tiers downgrade RenderTextureQuality:
- // <= 2GB -> Low(0), <= 4GB -> Medium(1), < 8GB -> High(2), >= 8GB -> Ultra(3).
- // When VRAM cannot be detected (mVRAM == 0, common on Linux) fall back to Medium.
- if (gGLManager.mVRAM == 0)
+ if (gGLManager.mVRAM < 2048)
{
- maskFeatures("VRAMLT4GB");
- }
- else
- {
- if (gGLManager.mVRAM < 8192)
- {
- maskFeatures("VRAMLT8GB");
- }
- if (gGLManager.mVRAM <= 4096)
- {
- maskFeatures("VRAMLT4GB");
- }
- if (gGLManager.mVRAM <= 2048)
- {
- maskFeatures("VRAMLT2GB");
- }
+ maskFeatures("VRAMLT2GB");
}
if (gGLManager.mGLVersion < 3.99f)
{
diff --git a/indra/newview/llfetchedgltfmaterial.cpp b/indra/newview/llfetchedgltfmaterial.cpp
index d71f0c1bd4..a05f725673 100644
--- a/indra/newview/llfetchedgltfmaterial.cpp
+++ b/indra/newview/llfetchedgltfmaterial.cpp
@@ -73,20 +73,6 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
LLViewerTexture* baseColorTex = media_tex ? media_tex : mBaseColorTexture;
LLViewerTexture* emissiveTex = media_tex ? media_tex : mEmissiveTexture;
- if (media_tex)
- {
- // Media hides these but they stay registered for coverage. Stamp them so
- // the GC doesn't coarsen/refetch them while the media is playing.
- if (mBaseColorTexture.notNull())
- {
- if (LLImageGL* gl_tex = mBaseColorTexture->getGLTexture()) { gl_tex->stampBound(); }
- }
- if (mEmissiveTexture.notNull())
- {
- if (LLImageGL* gl_tex = mEmissiveTexture->getGLTexture()) { gl_tex->stampBound(); }
- }
- }
-
if (!LLPipeline::sShadowRender || (mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK))
{
if (mAlphaMode == LLGLTFMaterial::ALPHA_MODE_MASK)
@@ -111,26 +97,13 @@ void LLFetchedGLTFMaterial::bind(LLViewerTexture* media_tex)
if (!LLPipeline::sShadowRender)
{
- // Bind the normal map at whatever resolution is resident, like Blinn-Phong
- // (soft, never absent). Only fall back to the flat normal when it's not
- // loaded yet. (The old discard<=4 gate dropped distant/tiled PBR normals
- // entirely, making PBR look flatter than equivalent Blinn content.)
- if (mNormalTexture.notNull() && mNormalTexture->hasGLTexture())
+ if (mNormalTexture.notNull() && mNormalTexture->getDiscardLevel() <= 4)
{
shader->bindTexture(LLShaderMgr::BUMP_MAP, mNormalTexture);
}
else
{
shader->bindTexture(LLShaderMgr::BUMP_MAP, LLViewerFetchedTexture::sFlatNormalImagep);
- if (mNormalTexture.notNull())
- {
- // In use, just not loaded yet - stamp it so the GC doesn't treat
- // it as unseen and pin it deep before its first real bind.
- if (LLImageGL* gl_tex = mNormalTexture->getGLTexture())
- {
- gl_tex->stampBound();
- }
- }
}
if (mMetallicRoughnessTexture.notNull())
diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp
index 92b3c92165..01108b5cfa 100644
--- a/indra/newview/llfloatersettingsdebug.cpp
+++ b/indra/newview/llfloatersettingsdebug.cpp
@@ -106,7 +106,6 @@ void LLFloaterSettingsDebug::onCommitSettings()
LLVector3 vector;
LLVector3d vectord;
- LLVector4 vector4;
LLQuaternion quat;
LLRect rect;
LLColor4 col4;
@@ -143,13 +142,6 @@ void LLFloaterSettingsDebug::onCommitSettings()
vectord.mdV[VZ] = mValSpinner3->getValue().asReal();
controlp->set(vectord.getValue());
break;
- case TYPE_VEC4:
- vector4.mV[VX] = (F32)mValSpinner1->getValue().asReal();
- vector4.mV[VY] = (F32)mValSpinner2->getValue().asReal();
- vector4.mV[VZ] = (F32)mValSpinner3->getValue().asReal();
- vector4.mV[VW] = (F32)mValSpinner4->getValue().asReal();
- controlp->set(vector4.getValue());
- break;
case TYPE_QUAT:
quat.mQ[VX] = mValSpinner1->getValueF32();
quat.mQ[VY] = mValSpinner2->getValueF32();
@@ -360,40 +352,6 @@ void LLFloaterSettingsDebug::updateControl(LLControlVariable* controlp)
}
break;
}
- case TYPE_VEC4:
- {
- LLVector4 v;
- v.setValue(sd);
- mValSpinner1->setVisible(true);
- mValSpinner1->setLabel(std::string("X"));
- mValSpinner2->setVisible(true);
- mValSpinner2->setLabel(std::string("Y"));
- mValSpinner3->setVisible(true);
- mValSpinner3->setLabel(std::string("Z"));
- mValSpinner4->setVisible(true);
- mValSpinner4->setLabel(std::string("W"));
- if (!mValSpinner1->hasFocus())
- {
- mValSpinner1->setPrecision(3);
- mValSpinner1->setValue(v[VX]);
- }
- if (!mValSpinner2->hasFocus())
- {
- mValSpinner2->setPrecision(3);
- mValSpinner2->setValue(v[VY]);
- }
- if (!mValSpinner3->hasFocus())
- {
- mValSpinner3->setPrecision(3);
- mValSpinner3->setValue(v[VZ]);
- }
- if (!mValSpinner4->hasFocus())
- {
- mValSpinner4->setPrecision(3);
- mValSpinner4->setValue(v[VW]);
- }
- break;
- }
case TYPE_QUAT:
{
LLQuaternion q;
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
index fe77d585c8..64359b6cbe 100644
--- a/indra/newview/llsurface.cpp
+++ b/indra/newview/llsurface.cpp
@@ -57,8 +57,6 @@ 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 ---------------
@@ -124,7 +122,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
@@ -585,13 +583,6 @@ 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++)
{
@@ -602,7 +593,6 @@ void LLSurface::updatePatchVisibilities(LLAgent &agent)
{
mVisiblePatchCount++;
patchp->updateCameraDistanceRegion(pos_region);
- sNearestVisiblePatchDistance = llmin(sNearestVisiblePatchDistance, patchp->getDistance());
}
}
}
@@ -971,7 +961,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 4bdb90b102..a599019ca5 100644
--- a/indra/newview/llsurface.h
+++ b/indra/newview/llsurface.h
@@ -163,12 +163,6 @@ 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 574c200eb4..51ade60827 100644
--- a/indra/newview/lltexturefetch.cpp
+++ b/indra/newview/lltexturefetch.cpp
@@ -542,7 +542,6 @@ private:
S32 mRequestedDiscard;
S32 mLoadedDiscard;
S32 mDecodedDiscard;
- S32 mCodecLevels = 0;
LLFrameTimer mRequestedDeltaTimer;
LLFrameTimer mFetchDeltaTimer;
LLTimer mCacheReadTimer;
@@ -1844,10 +1843,6 @@ 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);
@@ -2779,12 +2774,10 @@ 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,
- S32& codec_levels)
+ LLCore::HttpStatus& last_http_get_status)
{
LL_PROFILE_ZONE_SCOPED;
bool res = false;
- codec_levels = 0;
LLTextureFetchWorker* worker = getWorker(id);
if (worker)
{
@@ -2816,9 +2809,6 @@ 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 d75e16ab7c..851d6c11a0 100644
--- a/indra/newview/lltexturefetch.h
+++ b/indra/newview/lltexturefetch.h
@@ -106,8 +106,7 @@ 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,
- S32& codec_levels);
+ LLCore::HttpStatus& last_http_get_status);
// Threads: T*
bool updateRequestPriority(const LLUUID& id, F32 priority);
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 10b57f47da..cbe2dac52a 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -476,7 +476,7 @@ private:
void LLGLTexMemBar::draw()
{
- F32 pixel_to_texel_ratio = LLViewerTexture::sPixelToTexelRatio;
+ F32 discard_bias = LLViewerTexture::sDesiredDiscardBias;
F32 cache_usage = (F32)LLAppViewer::getTextureCache()->getUsage().valueInUnits<LLUnits::Megabytes>();
F32 cache_max_usage = (F32)LLAppViewer::getTextureCache()->getMaxUsage().valueInUnits<LLUnits::Megabytes>();
S32 line_height = LLFontGL::getFontMonospace()->getLineHeight();
@@ -560,20 +560,19 @@ void LLGLTexMemBar::draw()
gGL.color4f(0.f, 0.f, 0.f, 0.25f);
gl_rect_2d(-10, getRect().getHeight() + line_height*2 + 1, getRect().getWidth()+2, getRect().getHeight()+2);
- text = llformat("Est. Free: %d MB Sys Free: %d MB FBO: %d MB Probe#: %d Probe Mem: %d MB Px:Texel 1:%.2f Cache: %.1f/%.1f MB",
+ text = llformat("Est. Free: %d MB Sys Free: %d MB FBO: %d MB Probe#: %d Probe Mem: %d MB Bias: %.2f Cache: %.1f/%.1f MB",
(S32)LLViewerTexture::sFreeVRAMMegabytes,
LLMemory::getAvailableMemKB()/1024,
LLRenderTarget::sBytesAllocated/(1024*1024),
gPipeline.mReflectionMapManager.probeCount(),
gPipeline.mReflectionMapManager.probeMemory(),
- pixel_to_texel_ratio,
+ discard_bias,
cache_usage,
cache_max_usage);
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*8,
text_color, LLFontGL::LEFT, LLFontGL::TOP);
- text = llformat("Images: %d Raw: %d (%.2f MB) Saved: %d (%.2f MB) Aux: %d (%.2f MB)",
- image_count, raw_image_count, raw_image_bytes_MB,
+ text = llformat("Images: %d Raw: %d (%.2f MB) Saved: %d (%.2f MB) Aux: %d (%.2f MB)", image_count, raw_image_count, raw_image_bytes_MB,
saved_raw_image_count, saved_raw_image_bytes_MB,
aux_raw_image_count, aux_raw_image_bytes_MB);
LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height * 7,
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index dd2357ae8f..0c93b24751 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -112,63 +112,6 @@ static bool handleRenderAvatarMouselookChanged(const LLSD& newvalue)
return true;
}
-// Per-tier texture quality preset. Data-driven so adding a setting is
-// "add a column", and the tier values are visible side-by-side. Index is
-// RenderTextureQuality: 0=Low, 1=Medium, 2=High, 3=Ultra.
-namespace
-{
- struct TexturePreset
- {
- const char* name;
- U32 max_resolution;
- F32 pixel_to_texel_ratio; // TexturePixelToTexelRatio (R_max, texels per pixel)
- F32 pressure_tighten_rate; // TexturePressureTightenRate (ratio units/sec)
- F32 pressure_relax_rate; // TexturePressureRelaxRate (ratio units/sec)
- F32 channel_ratio_normal; // TextureChannelRatioNormal
- F32 channel_ratio_basecolor; // TextureChannelRatioBaseColor
- F32 channel_ratio_specular; // TextureChannelRatioSpecular
- F32 channel_ratio_emissive; // TextureChannelRatioEmissive
- };
-
- // Tier values: Low (2-4GB), Medium (4-8GB), High (8-16GB), Ultra (16+GB).
- // Quality ladder, expressed in pixel:texel (texels per pixel):
- // - pixel_to_texel_ratio is the baseline quality: how many texels per
- // screen pixel the tier allocates when VRAM is comfortable (1.0 = 1:1).
- // Under pressure the runtime drives the global ratio below this with no
- // floor (down to 0 = deepest mips), so there is no per-tier minimum.
- // - the channel ratios coarsen specular/emissive/normal relative to base
- // color (each is a multiplier on the global ratio).
- // The pressure water marks (TexturePressureHighWater/LowWater) are NOT tiered - they're a
- // physical "crossed the budget" threshold (0.90 / 0.70), constant across
- // tiers. Lower tiers start blurrier (lower R_max) and tighten faster.
- // max_res Rmax tight relax N BC S E
- static constexpr TexturePreset TEXTURE_PRESETS[4] = {
- /* 0 Low */ { "Low", 1024, 0.10f, 0.50f, 0.05f, 0.50f, 1.00f, 0.25f, 0.25f },
- /* 1 Medium */ { "Medium", 2048, 0.40f, 0.35f, 0.08f, 1.00f, 1.00f, 0.50f, 0.50f },
- /* 2 High */ { "High", 2048, 0.80f, 0.25f, 0.10f, 1.00f, 1.00f, 0.50f, 1.00f },
- /* 3 Ultra */ { "Ultra", 2048, 1.00f, 0.15f, 0.12f, 1.00f, 1.00f, 1.00f, 1.00f },
- };
-}
-
-static bool handleRenderTextureQualityChanged(const LLSD& newvalue)
-{
- U32 quality = (U32)newvalue.asInteger();
- if (quality > 3) quality = 3;
- const TexturePreset& p = TEXTURE_PRESETS[quality];
-
- gSavedSettings.setU32("RenderMaxTextureResolution", p.max_resolution);
- gSavedSettings.setF32("TexturePixelToTexelRatio", p.pixel_to_texel_ratio);
- gSavedSettings.setF32("TexturePressureTightenRate", p.pressure_tighten_rate);
- gSavedSettings.setF32("TexturePressureRelaxRate", p.pressure_relax_rate);
- gSavedSettings.setF32("TextureChannelRatioNormal", p.channel_ratio_normal);
- gSavedSettings.setF32("TextureChannelRatioBaseColor", p.channel_ratio_basecolor);
- gSavedSettings.setF32("TextureChannelRatioSpecular", p.channel_ratio_specular);
- gSavedSettings.setF32("TextureChannelRatioEmissive", p.channel_ratio_emissive);
-
- LL_INFOS("TextureStream") << "Applied texture quality preset: " << p.name << LL_ENDL;
- return true;
-}
-
static bool handleRenderFarClipChanged(const LLSD& newvalue)
{
if (LLStartUp::getStartupState() >= STATE_STARTED)
@@ -872,7 +815,6 @@ void settings_setup_listeners()
{
LL_PROFILE_ZONE_SCOPED;
setting_setup_signal_listener(gSavedSettings, "FirstPersonAvatarVisible", handleRenderAvatarMouselookChanged);
- setting_setup_signal_listener(gSavedSettings, "RenderTextureQuality", handleRenderTextureQualityChanged);
setting_setup_signal_listener(gSavedSettings, "RenderFarClip", handleRenderFarClipChanged);
setting_setup_signal_listener(gSavedSettings, "RenderTerrainScale", handleTerrainScaleChanged);
setting_setup_signal_listener(gSavedSettings, "RenderTerrainPBRScale", handlePBRTerrainScaleChanged);
@@ -884,6 +826,7 @@ void settings_setup_listeners()
setting_setup_signal_listener(gSavedSettings, "OctreeMaxNodeCapacity", handleRepartition);
setting_setup_signal_listener(gSavedSettings, "OctreeAlphaDistanceFactor", handleRepartition);
setting_setup_signal_listener(gSavedSettings, "OctreeAttachmentSizeFactor", handleRepartition);
+ setting_setup_signal_listener(gSavedSettings, "RenderMaxTextureIndex", handleSetShaderChanged);
setting_setup_signal_listener(gSavedSettings, "RenderUIBuffer", handleWindowResized);
setting_setup_signal_listener(gSavedSettings, "RenderDepthOfField", handleReleaseGLBufferChanged);
setting_setup_signal_listener(gSavedSettings, "RenderFSAAType", handleReleaseGLBufferChanged);
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index be83fd0279..0d50ba6fe2 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -140,27 +140,6 @@ void render_disconnected_background();
void getProfileStatsContext(boost::json::object& stats);
std::string getProfileStatsFilename();
-// Adaptive texture-pipeline budget: spend the frame-time headroom between the
-// frame we're rendering and TextureLoadTargetFPS, clamped [2ms, max]. Headroom
-// is measured (smoothed frame interval minus the pipeline's own last spend),
-// so fast machines get big budgets and machines already at target hold the
-// floor. Only consumed while queues have work - drain loops exit when empty.
-static F32 sTexturePipelineSpent = 0.f;
-static F32 texture_pipeline_budget()
-{
- static LLCachedControl<F32> target_fps(gSavedSettings, "TextureLoadTargetFPS", 60.f);
- static LLCachedControl<F32> max_ms(gSavedSettings, "TextureLoadBudgetMaxMS", 10.f);
- static F32 smoothed_other = 0.008f;
- F32 other = llmax(gFrameIntervalSeconds.value() - sTexturePipelineSpent, 0.f);
- // A single multi-second hitch must not crater the budget for the following
- // frames, so cap the sample before it enters the EMA.
- other = llmin(other, 0.1f);
- smoothed_other = smoothed_other * 0.9f + other * 0.1f;
- F32 target_interval = 1.f / llclamp((F32)target_fps, 15.f, 240.f);
- F32 headroom = target_interval - smoothed_other;
- return llclamp(headroom, 0.002f, llclamp((F32)max_ms, 2.f, 50.f) * 0.001f);
-}
-
void display_startup()
{
if ( !gViewerWindow
@@ -521,10 +500,9 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List");
- F32 max_image_decode_time = texture_pipeline_budget();
- LLTimer tex_timer;
+ F32 max_image_decode_time = 0.050f * gFrameIntervalSeconds.value(); // 50 ms/second decode time
+ max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f); // min 2ms/frame, max 5ms/frame)
gTextureList.updateImages(max_image_decode_time);
- sTexturePipelineSpent = tex_timer.getElapsedTimeF32();
}
{
@@ -884,10 +862,9 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List");
- F32 max_image_decode_time = texture_pipeline_budget();
- LLTimer tex_timer;
+ F32 max_image_decode_time = 0.050f*gFrameIntervalSeconds.value(); // 50 ms/second decode time
+ max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f ); // min 2ms/frame, max 5ms/frame)
gTextureList.updateImages(max_image_decode_time);
- sTexturePipelineSpent = tex_timer.getElapsedTimeF32();
}
{
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index 4e4282a8ca..7c26cb3c9f 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -5229,20 +5229,7 @@ void LLViewerObject::setTE(const U8 te, const LLTextureEntry& texture_entry)
const LLUUID& image_id = getTE(te)->getID();
LLViewerTexture* bakedTexture = getBakedTextureForMagicId(image_id);
- if (bakedTexture)
- {
- mTEImages[te] = bakedTexture;
- }
- else
- {
- LLViewerFetchedTexture* img = LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- // Same creation seed the PBR path applies in updateTEMaterialTextures'
- // fetch_texture - without it a Blinn texture has decode_priority 0 and
- // cannot even fetch headers until its first coverage measurement,
- // while the equivalent PBR texture starts fetching immediately.
- img->addTextureStats(64.f * 64.f, true);
- mTEImages[te] = img;
- }
+ mTEImages[te] = bakedTexture ? bakedTexture : LLViewerTextureManager::getFetchedTexture(image_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
updateAvatarMeshVisibility(image_id, old_image_id);
@@ -5253,14 +5240,11 @@ void LLViewerObject::updateTEMaterialTextures(U8 te)
{
if (getTE(te)->getMaterialParams().notNull())
{
- // Same creation seed as the PBR fetch_texture below - see setTE.
const LLUUID& norm_id = getTE(te)->getMaterialParams()->getNormalID();
mTENormalMaps[te] = LLViewerTextureManager::getFetchedTexture(norm_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- mTENormalMaps[te]->addTextureStats(64.f * 64.f, true);
const LLUUID& spec_id = getTE(te)->getMaterialParams()->getSpecularID();
mTESpecularMaps[te] = LLViewerTextureManager::getFetchedTexture(spec_id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
- mTESpecularMaps[te]->addTextureStats(64.f * 64.f, true);
}
LLFetchedGLTFMaterial* mat = (LLFetchedGLTFMaterial*) getTE(te)->getGLTFRenderMaterial();
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index 255317ce80..27865f7598 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -561,7 +561,13 @@ void LLViewerShaderMgr::setShaders()
LLAppViewer::instance()->isSecondInstance());
}
+ static LLCachedControl<U32> max_texture_index(gSavedSettings, "RenderMaxTextureIndex", 16);
+
+ // when using indexed texture rendering, leave some texture units available for shadow and reflection maps
+ static LLCachedControl<S32> reserved_texture_units(gSavedSettings, "RenderReservedTextureIndices", 14);
+
LLGLSLShader::sIndexedTextureChannels = 4;
+ //llclamp<S32>(max_texture_index, 1, gGLManager.mNumTextureImageUnits-reserved_texture_units);
reentrance = true;
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index 90facfa333..6e05555c45 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -60,7 +60,6 @@
#include "llmediaentry.h"
#include "llvovolume.h"
#include "llviewermedia.h"
-#include "lldrawable.h"
#include "lltexturecache.h"
#include "llviewerwindow.h"
#include "llwindow.h"
@@ -87,8 +86,8 @@ S32 LLViewerTexture::sImageCount = 0;
S32 LLViewerTexture::sRawCount = 0;
S32 LLViewerTexture::sAuxCount = 0;
LLFrameTimer LLViewerTexture::sEvaluationTimer;
-F32 LLViewerTexture::sPixelToTexelRatio = 1.f;
-U32 LLViewerTexture::sGCSuspendedFrame = 0;
+F32 LLViewerTexture::sDesiredDiscardBias = 0.f;
+U32 LLViewerTexture::sBiasTexturesUpdated = 0;
S32 LLViewerTexture::sMaxSculptRez = 128; //max sculpt image size
constexpr S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64;
@@ -98,11 +97,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 +120,7 @@ LLLoadedCallbackEntry::LLLoadedCallbackEntry(loaded_callback_func cb,
LLViewerFetchedTexture* target,
bool pause)
: mCallback(cb),
- mLastUsedDiscard(S32_MAX),
+ mLastUsedDiscard(MAX_DISCARD_LEVEL+1),
mDesiredDiscard(discard_level),
mNeedsImageRaw(need_imageraw),
mUserData(userdata),
@@ -485,13 +485,6 @@ 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)
{
@@ -512,89 +505,178 @@ void LLViewerTexture::updateClass()
// get an estimate of how much video memory we're using
// NOTE: our metrics miss about half the vram we use, so this biases high but turns out to typically be within 5% of the real number
- F32 vram_used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
+ F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
// For debugging purposes, it's useful to be able to set the VRAM budget manually.
// But when manual control is not enabled, use the VRAM divisor.
// While we're at it, assume we have 1024 to play with at minimum when the divisor is in use. Works more elegantly with the logic below this.
// -Geenz 2025-03-21
- F32 vram_budget = max_vram_budget == 0 ? llmax(1024, (F32)gGLManager.mVRAM / tex_vram_divisor) : (F32)max_vram_budget;
+ F32 budget = max_vram_budget == 0 ? llmax(1024, (F32)gGLManager.mVRAM / tex_vram_divisor) : (F32)max_vram_budget;
// Try to leave at least half a GB for everyone else and for bias,
// but keep at least 768MB for ourselves
// Viewer can 'overshoot' target when scene changes, if viewer goes over budget it
// can negatively impact performance, so leave 20% of a breathing room for
// 'bias' calculation to kick in.
- F32 vram_target = llmax(llmin(vram_budget - 512.f, vram_budget * 0.8f), MIN_VRAM_BUDGET);
- sFreeVRAMMegabytes = vram_target - vram_used;
+ F32 target = llmax(llmin(budget - 512.f, budget * 0.8f), MIN_VRAM_BUDGET);
+ sFreeVRAMMegabytes = target - used;
- // VRAM pressure controller for the global pixel:texel ratio. Tightens above
- // the high watermark, relaxes below the low one, holds in the band (the band
- // stops sawtooth). Rates are slow so eviction frees bytes before the next step.
- {
- static LLCachedControl<F32> ratio_max(gSavedSettings, "TexturePixelToTexelRatio", 1.0f);
- static LLCachedControl<F32> bg_min_ratio(gSavedSettings, "TextureBackgroundMinRatio", 0.001f);
- static LLCachedControl<F32> wm_high(gSavedSettings, "TexturePressureHighWater", 0.90f);
- static LLCachedControl<F32> wm_low(gSavedSettings, "TexturePressureLowWater", 0.70f);
- static LLCachedControl<F32> tighten_rate(gSavedSettings, "TexturePressureTightenRate", 0.30f);
- static LLCachedControl<F32> relax_rate(gSavedSettings, "TexturePressureRelaxRate", 0.10f);
- static LLCachedControl<F32> cooldown_step(gSavedSettings, "TextureCooldownStepSeconds", 5.f);
+ F32 over_pct = (used - target) / target;
- // No lower bound: pressure can drive the ratio to 0 (deepest mip for
- // everything). Only the top is capped, by the configured max.
- F32 r_max = llmax((F32)ratio_max, 0.f);
- F32 high_frac = llclamp((F32)wm_high, 0.1f, 1.f);
- F32 high = vram_budget * high_frac;
- F32 low = vram_budget * llclamp((F32)wm_low, 0.05f, high_frac);
- F32 dt = (F32)gFrameIntervalSeconds;
+ bool is_sys_low = isSystemMemoryLow();
+ bool is_low = is_sys_low || over_pct > 0.f;
- bool in_background = (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) || !gFocusMgr.getAppHasFocus();
+ static bool was_low = false;
- if (in_background)
+ if (is_low && !was_low)
+ {
+ if (is_sys_low)
{
- // Backgrounded: decay toward bg_min to free VRAM for other apps, one
- // mip per cooldown_step seconds (multiplicative). Don't relax back up
- // until we're focused again. Pressure can still push below bg_min.
- F32 bg_min = llclamp((F32)bg_min_ratio, 0.f, r_max);
- if (sPixelToTexelRatio > bg_min)
- {
- const F32 step = llmax((F32)cooldown_step, 0.01f);
- sPixelToTexelRatio = llmax(sPixelToTexelRatio * powf(0.25f, dt / step), bg_min);
- }
+ // Not having system memory is more serious, so discard harder
+ sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.5f * LLMemory::getSystemMemoryBudgetFactor());
}
- else if (vram_used > high)
+ else
{
- sPixelToTexelRatio -= llmax((F32)tighten_rate, 0.f) * dt;
+ // Slam to 1.5 bias the moment we hit low memory (discards off screen textures immediately)
+ sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.5f);
+ }
+
+ if (is_sys_low || over_pct > 2.f)
+ { // if we're low on system memory, emergency purge off screen textures to avoid a death spiral
+ LL_WARNS() << "Low system memory detected, emergency downrezzing off screen textures" << LL_ENDL;
+ for (auto& image : gTextureList)
+ {
+ gTextureList.updateImageDecodePriority(image, false /*will modify gTextureList otherwise!*/);
+ }
}
- else if (vram_used < low)
+ }
+
+ was_low = is_low;
+
+ if (is_low)
+ {
+ // ramp up discard bias over time to free memory
+ if (sEvaluationTimer.getElapsedTimeF32() > MEMORY_CHECK_WAIT_TIME)
{
- sPixelToTexelRatio += llmax((F32)relax_rate, 0.f) * dt;
+ static LLCachedControl<F32> low_mem_min_discard_increment(gSavedSettings, "RenderLowMemMinDiscardIncrement", .1f);
+
+ F32 increment = low_mem_min_discard_increment + llmax(over_pct, 0.f);
+ sDesiredDiscardBias += increment * gFrameIntervalSeconds;
}
- // else: hold in the hysteresis band.
+ }
+ else
+ {
+ // don't execute above until the slam to 1.5 has a chance to take effect
+ sEvaluationTimer.reset();
- // Allocation failures outrank the byte estimate: if setManualImage hit
- // GL_OUT_OF_MEMORY since last frame, the CPU-side vram_used estimate has
- // diverged from reality, so step the ratio down now regardless of the band.
- static U32 last_oom_count = 0;
- U32 oom_count = LLImageGL::sOOMErrorCount.load();
- if (oom_count > last_oom_count)
+ // 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;
+ static LLCachedControl<U32> min_free_main_memory(gSavedSettings, "RenderMinFreeMainMemoryThreshold", 512);
+ 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)
{
- U32 new_events = llmin(oom_count - last_oom_count, (U32)5);
- sPixelToTexelRatio -= llmax((F32)tighten_rate, 0.f) * 1.0f * (F32)new_events;
- last_oom_count = oom_count;
- LL_WARNS_ONCE("Texture") << "GL out-of-memory during texture upload triggered a pixel:texel ratio backoff." << LL_ENDL;
+ static LLCachedControl<F32> high_mem_discard_decrement(gSavedSettings, "RenderHighMemMinDiscardDecrement", .1f);
+
+ F32 decrement = high_mem_discard_decrement - llmin(over_pct - FREE_PERCENTAGE_TRESHOLD, 0.f);
+ sDesiredDiscardBias -= decrement * gFrameIntervalSeconds;
}
+ }
- sPixelToTexelRatio = llclamp(sPixelToTexelRatio, 0.f, r_max);
+ // set to max discard bias if the window has been backgrounded for a while
+ static F32 last_desired_discard_bias = 1.f;
+ static F32 last_texture_update_count_bias = 1.f;
+ static bool was_backgrounded = false;
+ static LLFrameTimer backgrounded_timer;
+ static LLCachedControl<F32> minimized_discard_time(gSavedSettings, "TextureDiscardMinimizedTime", 1.f);
+ static LLCachedControl<F32> backgrounded_discard_time(gSavedSettings, "TextureDiscardBackgroundedTime", 60.f);
- // Keep the GC-suspend frame current while backgrounded. This suppresses
- // the foreground GC now, and gives it a grace window after we come back so
- // visible content can re-stamp its bind frames before anything is collected.
- if (in_background)
+ bool in_background = (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) || !gFocusMgr.getAppHasFocus();
+ bool is_minimized = gViewerWindow && gViewerWindow->getWindow()->getMinimized() && in_background;
+ if (in_background)
+ {
+ F32 discard_time = is_minimized ? minimized_discard_time : backgrounded_discard_time;
+ if (discard_time > 0.f && backgrounded_timer.getElapsedTimeF32() > discard_time)
{
- sGCSuspendedFrame = LLFrameTimer::getFrameCount();
+ if (!was_backgrounded)
+ {
+ LL_INFOS() << "Viewer was " << (is_minimized ? "minimized" : "backgrounded") << " for " << discard_time
+ << "s, freeing up video memory." << LL_ENDL;
+
+ last_desired_discard_bias = sDesiredDiscardBias;
+ was_backgrounded = true;
+ }
+ sDesiredDiscardBias = 5.f;
+ }
+ }
+ else
+ {
+ backgrounded_timer.reset();
+ if (was_backgrounded)
+ { // if the viewer was backgrounded
+ LL_INFOS() << "Viewer is no longer backgrounded or minimized, resuming normal texture usage." << LL_ENDL;
+ was_backgrounded = false;
+ sDesiredDiscardBias = last_desired_discard_bias;
}
}
+
+ sDesiredDiscardBias = llclamp(sDesiredDiscardBias, 1.f, 4.f);
+ if (last_texture_update_count_bias < sDesiredDiscardBias)
+ {
+ // bias increased, reset texture update counter to
+ // let updates happen at an increased rate.
+ last_texture_update_count_bias = sDesiredDiscardBias;
+ sBiasTexturesUpdated = 0;
+ }
+ else if (last_texture_update_count_bias > sDesiredDiscardBias + 0.1f)
+ {
+ // bias decreased, 0.1f is there to filter out small fluctuations
+ // and not reset sBiasTexturesUpdated too often.
+ // Bias jumps to 1.5 at low memory, so getting stuck at 1.1 is not
+ // a problem.
+ last_texture_update_count_bias = sDesiredDiscardBias;
+ }
+
+ LLViewerTexture::sFreezeImageUpdates = false;
+}
+
+//static
+U32Megabytes LLViewerTexture::getFreeSystemMemory()
+{
+ static LLFrameTimer timer;
+ static U32Megabytes physical_res = U32Megabytes(U32_MAX);
+
+ if (timer.getElapsedTimeF32() < MEMORY_CHECK_WAIT_TIME) //call this once per second.
+ {
+ return physical_res;
+ }
+
+ timer.reset();
+
+ LLMemory::updateMemoryInfo();
+ physical_res = LLMemory::getAvailableMemKB();
+ return physical_res;
+}
+
+S32Megabytes get_render_free_main_memory_treshold()
+{
+ static LLCachedControl<U32> min_free_main_memory(gSavedSettings, "RenderMinFreeMainMemoryThreshold", 512);
+ const U32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory);
+ return MIN_FREE_MAIN_MEMORY;
+}
+
+//static
+bool LLViewerTexture::isSystemMemoryLow()
+{
+ return getFreeSystemMemory() < get_render_free_main_memory_treshold();
+}
+
+//static
+bool LLViewerTexture::isSystemMemoryCritical()
+{
+ return getFreeSystemMemory() < get_render_free_main_memory_treshold() / 2;
}
//end of static functions
@@ -1060,10 +1142,8 @@ void LLViewerFetchedTexture::init(bool firstinit)
mRequestedDownloadPriority = 0.f;
mFullyLoaded = false;
mCanUseHTTP = true;
- 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;
+ mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
+ mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
mDecodingAux = false;
@@ -1497,16 +1577,6 @@ void LLViewerFetchedTexture::postCreateTexture()
setActive();
- // Start the visibility-GC clock at creation. A texture fetched but never
- // drawn (occluded, or the camera moved on) would otherwise keep
- // mLastBindFrame == 0 forever, and the GC skips never-bound textures - it
- // would hold residency indefinitely. Anchoring here means it ages out on
- // the normal GC cooldown unless a real draw stamps it first.
- if (mGLTexturep.notNull() && mGLTexturep->mLastBindFrame == 0)
- {
- mGLTexturep->mLastBindFrame = LLFrameTimer::getFrameCount();
- }
-
// rebuild any volumes that are using this texture for sculpts in case their LoD has changed
for (U32 i = 0; i < mNumVolumes[LLRender::SCULPT_TEX]; ++i)
{
@@ -1893,10 +1963,21 @@ bool LLViewerFetchedTexture::processFetchResults(S32& desired_discard, S32 curre
setIsMissingAsset();
desired_discard = -1;
}
- // 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).
+ 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;
+ }
+ }
destroyRawImage();
}
else if (mRawImage.notNull())
@@ -1966,14 +2047,7 @@ bool LLViewerFetchedTexture::updateFetch()
S32 current_discard = getCurrentDiscardLevelForFetching();
S32 desired_discard = getDesiredDiscardLevel();
-
- // Two-tier fetch priority, constantly drained by the fetch worker (it
- // sorts HTTP dispatch by this value): any on-screen texture outranks every
- // off-screen one. Within the visible tier, coverage orders by size on
- // screen; within the off-screen tier the same geometric coverage
- // (area/dist^2) orders by proximity. addTextureStats clamps
- // mMaxVirtualSize to sMaxVirtualSize, so the band offset is strict.
- F32 decode_priority = mMaxVirtualSize + (mOnScreen ? sMaxVirtualSize : 0.f);
+ F32 decode_priority = mMaxVirtualSize;
if (mIsFetching)
{
@@ -1984,18 +2058,8 @@ 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, 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;
- }
- }
+ mLastHttpGetStatus);
if (mRawImage.notNull()) sRawCount++;
if (mAuxRawImage.notNull())
{
@@ -2030,32 +2094,7 @@ bool LLViewerFetchedTexture::updateFetch()
}
}
- // 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);
-
- // Progressive refinement: when resident data is much coarser than desired,
- // fetch in steps of TextureFetchStepMips instead of jumping straight to the
- // final discard. Each step is small, decodes fast, and shows on screen
- // while the next chains behind it (the per-frame fast pump makes chaining
- // nearly free). Without this, seen-before textures (dims known, so the
- // coarse first-fetch fallback never applies) sat blurry for the whole
- // full-file read+decode, then popped. Boosted/pinned content still jumps.
- static LLCachedControl<U32> fetch_step(gSavedSettings, "TextureFetchStepMips", 2);
- const S32 step = (S32)fetch_step;
- if (step > 0
- && current_discard >= 0
- && desired_discard < current_discard - step
- && mBoostLevel < LLGLTexture::BOOST_HIGH
- && mUseMipMaps
- && !mDontDiscard
- && !isAgentAvatarBoost(mBoostLevel))
- {
- // Re-clamp: current can sit past codec max after scaleDown (GL
- // pyramid goes deeper than the codestream) - a stepped request
- // above codec max reaches the decoder with an invalid discard.
- desired_discard = llmin(current_discard - step, (S32)mCodecMaxDiscardLevel);
- }
+ desired_discard = llmin(desired_discard, getMaxDiscardLevel());
bool make_request = true;
if (decode_priority <= 0)
@@ -2063,13 +2102,9 @@ bool LLViewerFetchedTexture::updateFetch()
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - priority <= 0");
make_request = false;
}
- else if (mDesiredDiscardLevel > (S32)mCodecMaxDiscardLevel &&
- current_discard >= 0)
+ else if (mDesiredDiscardLevel > getMaxDiscardLevel())
{
- // Desired is past codec_max. Only scaleDown can satisfy it.
- // Applies even when current is also past codec_max (post-scaleDown);
- // re-fetching at codec_max then scaleDown-ing again is pure thrash.
- LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > codec max");
+ LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > max");
make_request = false;
}
else if (mNeedsCreateTexture || mIsMissingAsset)
@@ -2082,9 +2117,6 @@ bool LLViewerFetchedTexture::updateFetch()
LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - current < min");
make_request = false;
}
- // No visibility gate here: off-screen content still fetches, just in the
- // low-priority band (decode_priority above), so the worker services it only
- // after visible work. Residency stays with the GC (computeDesiredDiscard).
if (make_request)
{
@@ -2140,7 +2172,6 @@ bool LLViewerFetchedTexture::updateFetch()
// in some cases createRequest can modify discard, as an example
// bake textures are always at discard 0
mRequestedDiscardLevel = llmin(desired_discard, fetch_request_response);
-
mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP);
}
@@ -2525,21 +2556,20 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
S32 gl_discard = getDiscardLevel();
- // S32_MAX is the "no data" sentinel; real discards can now exceed
- // MAX_DISCARD_LEVEL via dimDerivedMaxDiscard.
+ // If we don't have a legit GL image, set it to be lower than the worst discard level
if (gl_discard == -1)
{
- gl_discard = S32_MAX;
+ gl_discard = MAX_DISCARD_LEVEL + 1;
}
//
// 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 = S32_MAX; // We can always do a readback to get a raw discard
+ S32 current_raw_discard = MAX_DISCARD_LEVEL + 1; // 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 = S32_MAX;
- S32 best_aux_discard = S32_MAX;
+ S32 current_aux_discard = MAX_DISCARD_LEVEL + 1;
+ S32 best_aux_discard = MAX_DISCARD_LEVEL + 1;
LLImageRaw *current_raw_image = nullptr;
if (mIsRawImageValid)
@@ -2639,7 +2669,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
//
// Run raw/auxiliary data callbacks
//
- if (run_raw_callbacks && current_raw_image != nullptr && current_raw_discard != S32_MAX)
+ if (run_raw_callbacks && current_raw_image != nullptr && (current_raw_discard <= getMaxDiscardLevel()))
{
// Do callbacks which require raw image data.
//LL_INFOS() << "doLoadedCallbacks raw for " << getID() << LL_ENDL;
@@ -2679,7 +2709,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks()
//
// Run GL callbacks
//
- if (run_gl_callbacks && gl_discard != S32_MAX)
+ if (run_gl_callbacks && (gl_discard <= getMaxDiscardLevel()))
{
//LL_INFOS() << "doLoadedCallbacks GL for " << getID() << LL_ENDL;
@@ -2982,172 +3012,9 @@ S8 LLViewerLODTexture::getType() const
return LLViewerTexture::LOD_TEXTURE;
}
-// Desired discard from the pixel:texel ratio - this is the entire streaming
-// policy. For each channel bucket the texture is used in, the most-demanding
-// (largest screen coverage) face sets that bucket's requirement at
-// floor(log4(texels / (R_global * channelRatio[b] * coverage))); the sharpest
-// requirement across buckets wins, since one GL image serves every channel it
-// is used in. floor (not ceil) keeps content native up close - "1:1" is a
-// target, not a hard cap that downrezzes anything slightly oversampled. A
-// per-mip hysteresis dead-band against the current discard level prevents
-// fetch/scaleDown thrash as an object slowly crosses a mip boundary.
-S32 LLViewerLODTexture::computeDesiredDiscard(S32 dim_max_i, bool avatar_bake) const
+bool LLViewerLODTexture::isUpdateFrozen()
{
- static const F64 log_4 = log(4.0);
-
- // UI-pinned (icons / thumbnails): size against the known draw size, with no
- // ratio or pressure - these want exact native-for-their-slot resolution.
- if (mKnownDrawWidth && mKnownDrawHeight)
- {
- S32 draw_texels = llclamp(mKnownDrawWidth * mKnownDrawHeight, MIN_IMAGE_AREA, MAX_IMAGE_AREA);
- S32 d = (draw_texels >= (S32)mTexelsPerImage)
- ? 0
- : (S32)floor(log((F64)mTexelsPerImage / (F64)draw_texels) / log_4);
- return llclamp(d, 0, dim_max_i);
- }
-
- // Avatar bakes ignore the global pressure ramp (a blurred bake reads as a
- // cloud avatar); they always size against the configured max ratio.
- static LLCachedControl<F32> ratio_max(gSavedSettings, "TexturePixelToTexelRatio", 1.0f);
- const F32 r_global = avatar_bake ? llmax((F32)ratio_max, 0.01f) : sPixelToTexelRatio;
-
- static LLCachedControl<F32> ch_normal (gSavedSettings, "TextureChannelRatioNormal", 1.0f);
- static LLCachedControl<F32> ch_basecolor(gSavedSettings, "TextureChannelRatioBaseColor", 1.0f);
- static LLCachedControl<F32> ch_specular (gSavedSettings, "TextureChannelRatioSpecular", 0.5f);
- static LLCachedControl<F32> ch_emissive (gSavedSettings, "TextureChannelRatioEmissive", 0.5f);
- const F32 channel_ratio[4] = { (F32)ch_normal, (F32)ch_basecolor, (F32)ch_specular, (F32)ch_emissive };
-
- // Downrez bias: 0 sizes each bucket to its most demanding use (the lowest
- // texels-per-pixel variant - the quality bound); 1 sizes to its least
- // demanding use (the most oversampled variant - frees the most memory).
- // Values between lerp across the texture's measured coverage spread.
- static LLCachedControl<F32> downrez_bias(gSavedSettings, "TextureDownrezCoverageBias", 0.25f);
- const F32 cov_bias = llclampf((F32)downrez_bias);
-
- // Continuous ideal discard = sharpest (smallest) requirement across the
- // channels this texture is actually used in.
- F32 ideal = (F32)dim_max_i; // default: coarsest, until a measurement arrives
- bool measured = false;
- for (S32 b = 0; b < 4; ++b)
- {
- F32 coverage = mChannelCoverage[b];
- if (coverage <= 0.f)
- {
- continue;
- }
- if (cov_bias > 0.f && mChannelCoverageMin[b] > 0.f && mChannelCoverageMin[b] < coverage)
- {
- // Geometric (log-space) lerp between the coverage bounds. The
- // consumer below is log4(coverage), and min/max are routinely
- // orders of magnitude apart - a linear pixel-area lerp barely
- // moves the resulting discard until bias approaches 1, then
- // plunges (reads as binary). Interpolating the RATIO instead
- // moves the discard linearly with bias: 0.5 = halfway between
- // the two ends in mip levels.
- coverage *= powf(mChannelCoverageMin[b] / coverage, cov_bias);
- }
- measured = true;
- F32 allowed_texels = coverage * r_global * llmax(channel_ratio[b], 0.01f);
- F32 d;
- if (allowed_texels <= 0.f) // ratio driven to 0 -> deepest mip
- d = (F32)dim_max_i;
- else if (allowed_texels >= (F32)mTexelsPerImage)
- d = 0.f;
- else
- d = (F32)(log((F64)mTexelsPerImage / (F64)allowed_texels) / log_4);
- ideal = llmin(ideal, d);
- }
- if (!measured)
- {
- return dim_max_i; // off-screen / never measured -> coarsest mip
- }
-
- // Round toward sharper (floor): a texture stays at a mip level until its
- // ideal is a full level past the boundary. This is what makes "1:1" mean
- // "native up close" - ceil would downrez anything even slightly oversampled
- // (a 2048 map can't hit its own resolution on a 1080p screen), which reads
- // as everything being blurry. Pressure still evicts by lowering R_global.
- ideal = llmax(ideal, 0.f);
-
- // Frustum allowance: a falloff on how unloaded out-of-view content gets,
- // by how far out it is. Grazing the edge keeps full resolution; at
- // TextureFrustumAllowance (fraction of a screen) past the edge it reaches
- // the deepest mip, lerped between. Keeps barely-out-of-view textures
- // resident so a camera swing back doesn't refetch them. Applied to the
- // continuous ideal so it shares the hysteresis dead-band below - applied
- // after it, camera motion made desired flap a mip at a time and churned
- // the fetch/scaleDown queues.
- static LLCachedControl<F32> frustum_allowance(gSavedSettings, "TextureFrustumAllowance", 0.2f);
- if (!avatar_bake && mFrustumOverflow > 0.f)
- {
- const F32 f = llclamp(mFrustumOverflow / llmax((F32)frustum_allowance, 0.01f), 0.f, 1.f);
- ideal += f * ((F32)dim_max_i - ideal);
- mLastOffScreenFrame = LLFrameTimer::getFrameCount();
- }
-
- const S32 target = (S32)floor(ideal);
-
- // Hysteresis: a texture at discard C is "happy" while floor(ideal) == C,
- // i.e. ideal in [C, C+1). Only leave that band once ideal is past it by the
- // margin, so coverage jitter at a boundary doesn't ping-pong fetch<->scaleDown.
- static LLCachedControl<F32> uprez_margin(gSavedSettings, "TextureUpRezMargin", 0.2f);
- const F32 margin = llclamp((F32)uprez_margin, 0.f, 0.9f);
- const S32 current = getDiscardLevel();
- S32 desired;
- if (current < 0)
- {
- desired = target; // nothing loaded yet
- }
- else if (ideal >= (F32)current + 1.f + margin)
- {
- desired = target; // clearly coarser -> evict
- }
- else if (ideal <= (F32)current - margin)
- {
- desired = target; // clearly finer -> uprez
- }
- else
- {
- desired = current; // inside the dead-band -> hold
- }
-
- // Foreground visibility GC (avatar bakes exempt). Background degradation is
- // handled by the ratio decay in updateClass, and the GC self-suppresses while
- // backgrounded via the sGCSuspendedFrame check below, so the two don't fight.
- //
- // For every gc_cooldown frames a texture goes without a camera bind, drop its
- // mip by gc_step, walking gradually toward the deepest mip instead of slamming.
- // Content drawn within the last cooldown stays full-res, so a fast camera pan
- // finds it only a step or two coarse on the way back. Resets when drawn again.
- //
- // Out-of-frustum content is governed by the frustum allowance above instead
- // (spatial falloff, not bind staleness) - without this exclusion the GC would
- // walk barely-out-of-view content to the deepest mip within a second and the
- // allowance would protect nothing.
- if (!avatar_bake && mFrustumOverflow <= 0.f)
- {
- if (LLImageGL* gli = getGLTexture())
- {
- static LLCachedControl<U32> gc_cooldown_frames(gSavedSettings, "TextureGCStepFrames", 5);
- static LLCachedControl<U32> gc_step_mips(gSavedSettings, "TextureGCStepMips", 1);
- constexpr U32 GC_RESUME_GRACE_FRAMES = 10;
- const U32 now = LLFrameTimer::getFrameCount();
- if (gli->mLastBindFrame > 0 // drawn, or anchored at creation (postCreateTexture)
- && now - sGCSuspendedFrame > GC_RESUME_GRACE_FRAMES // not just back from background
- && now - mLastOffScreenFrame > GC_RESUME_GRACE_FRAMES) // re-entering content gets one grace window to be drawn and re-stamp before staleness is judged
- {
- const U32 cooldown = llmax((U32)gc_cooldown_frames, 1u);
- const S32 periods = (S32)((now - gli->mLastBindFrame) / cooldown);
- if (periods > 0)
- {
- const S32 step_mips = (S32)llmax((U32)gc_step_mips, 1u);
- desired = llclamp(desired + periods * step_mips, desired, dim_max_i);
- }
- }
- }
- }
-
- return llclamp(desired, 0, dim_max_i);
+ return LLViewerTexture::sFreezeImageUpdates;
}
// This is gauranteed to get called periodically for every texture
@@ -3157,13 +3024,7 @@ void LLViewerLODTexture::processTextureStats()
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
updateVirtualSize();
- // Hoisted once: avatar bake textures are exempt from several pressure
- // mechanisms below (anti-cloud-bug protection). Reads of `avatar_bake`
- // replace inline isAgentAvatarBoost(mBoostLevel) calls; each site keeps
- // its own intent comment explaining *why* the exemption applies. See
- // also LLViewerFetchedTexture::isAgentAvatarBoost() in the header for
- // the canonical list of exemption sites.
- const bool avatar_bake = isAgentAvatarBoost(mBoostLevel);
+ bool did_downscale = false;
static LLCachedControl<bool> textures_fullres(gSavedSettings,"TextureLoadFullRes", false);
@@ -3181,15 +3042,18 @@ void LLViewerLODTexture::processTextureStats()
{
mDesiredDiscardLevel = 0;
}
- // HUD/UI/preview and mDontDiscard textures bypass streaming - no
- // coverage signal applies, they need native resolution.
- else if (mBoostLevel >= LLGLTexture::BOOST_HIGH
- || mDontDiscard
- || !mUseMipMaps)
+ // Generate the request priority and render priority
+ else if (mDontDiscard || !mUseMipMaps)
{
mDesiredDiscardLevel = 0;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
- mDesiredDiscardLevel = 1; // 4096^2 source can't be loaded full res
+ 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);
}
else if (!mFullWidth || !mFullHeight)
{
@@ -3198,39 +3062,65 @@ void LLViewerLODTexture::processTextureStats()
}
else
{
- // Per-texture max discard (smallest meaningful mip): floor(log2(max(w,h))).
- S32 dim_max_for_image_i = (mFullWidth > 0 && mFullHeight > 0)
- ? LLImageGL::dimDerivedMaxDiscard(mFullWidth, mFullHeight)
- : (S32)mCodecMaxDiscardLevel;
+ //static const F64 log_2 = log(2.0);
+ static const F64 log_4 = log(4.0);
- // The whole policy: pixel:texel ratio at the most-demanding face.
- S32 discard = computeDesiredDiscard(dim_max_for_image_i, avatar_bake);
+ F32 discard_level = 0.f;
- // Per-texture caps: force >=1 for sources over the resolution cap;
- // bound by the debug override or the dim-derived max.
- S32 min_discard = 0;
+ // 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.
+ if (mKnownDrawWidth && mKnownDrawHeight)
+ {
+ 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);
+ }
+
+ discard_level = floorf(discard_level);
+
+ F32 min_discard = 0.f;
if (mFullWidth > max_tex_res || mFullHeight > max_tex_res)
- min_discard = 1;
+ min_discard = 1.f;
- static LLCachedControl<S32> max_discard_override(gSavedSettings, "TextureMaxDiscardOverride", 0);
- const S32 effective_cap = (max_discard_override > 0) ? (S32)max_discard_override : dim_max_for_image_i;
- discard = llclamp(discard, min_discard, effective_cap);
+ discard_level = llclamp(discard_level, min_discard, (F32)MAX_DISCARD_LEVEL);
- // Caller-set min-discard ceiling (terrain / avatar-self / thumbnails):
- // never coarser than the caller explicitly asked for.
- discard = llmin(discard, (S32)mMinDesiredDiscardLevel);
+ // 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);
- mDesiredDiscardLevel = (S8)discard;
+ //
+ // At this point we've calculated the quality level that we want,
+ // if possible. Now we check to see if we have it, and take the
+ // proper action if we don't.
+ //
- // If the GPU already holds finer data than we now want, evict it.
- // Avatar bakes exempt: shrinking mid-bake can leave the avatar stuck
- // as a cloud until the next bake completes.
S32 current_discard = getDiscardLevel();
- if (!avatar_bake && current_discard >= 0 && current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage)
+ if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED)
{
- scaleDown();
+ if (current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage)
+ { // should scale down
+ scaleDown();
+ }
}
+ 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);
}
@@ -3256,17 +3146,6 @@ bool LLViewerLODTexture::scaleDown()
return false;
}
- // Structural blocks only; per-texture policy lives in processTextureStats.
- if (!mUseMipMaps || mDontDiscard || mBoostLevel >= LLGLTexture::BOOST_HIGH)
- {
- return false;
- }
- // Avatar bakes are exempt from mid-bake eviction (cloud avatar risk).
- if (isAgentAvatarBoost(mBoostLevel))
- {
- return false;
- }
-
if (!mDownScalePending)
{
mDownScalePending = true;
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 90993ed109..20aaa8a058 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -114,6 +114,8 @@ protected:
public:
static void initClass();
static void updateClass();
+ static bool isSystemMemoryLow();
+ static bool isSystemMemoryCritical();
LLViewerTexture(bool usemipmaps = true);
LLViewerTexture(const LLUUID& id, bool usemipmaps) ;
@@ -145,11 +147,6 @@ public:
virtual F32 getMaxVirtualSize() ;
- // Read-only debug access to the per-bucket coverage bounds (see
- // mChannelCoverage) - used by the RENDER_DEBUG_TEXTURE_PRIORITY overlay.
- F32 getChannelCoverage(S32 bucket) const { return (bucket >= 0 && bucket < 4) ? mChannelCoverage[bucket] : 0.f; }
- F32 getChannelCoverageMin(S32 bucket) const { return (bucket >= 0 && bucket < 4) ? mChannelCoverageMin[bucket] : 0.f; }
-
LLFrameTimer* getLastReferencedTimer() { return &mLastReferencedTimer; }
S32 getFullWidth() const { return mFullWidth; }
@@ -193,6 +190,8 @@ private:
friend class LLBumpImageList;
friend class LLUIImageList;
+ static U32Megabytes getFreeSystemMemory();
+
protected:
friend class LLViewerTextureList;
LLUUID mID;
@@ -203,37 +202,6 @@ protected:
mutable S32 mMaxVirtualSizeResetInterval;
LLFrameTimer mLastReferencedTimer;
- // Screen-space pixel coverage bounds among the texture's faces, per
- // priority bucket (0=Normal, 1=BaseColor, 2=Specular, 3=Emissive). 0 = the
- // texture is not used in that channel (or hasn't been measured yet).
- // Populated by LLViewerTextureList::updateImageDecodePriority; consumed by
- // LLViewerLODTexture::computeDesiredDiscard. This is the only view-dependent
- // streaming signal - distance, size, tiling, and channel role all collapse
- // into it. Max = the most demanding use (lowest texels-per-pixel variant,
- // the quality bound); Min = the least demanding positive use (highest
- // texels-per-pixel, most oversampled - the downrez-bias end).
- F32 mChannelCoverage[4] = { 0.f, 0.f, 0.f, 0.f };
- F32 mChannelCoverageMin[4] = { 0.f, 0.f, 0.f, 0.f };
-
- // Any face using this texture projects onto the screen (published alongside
- // the coverage above). Selects the fetch-priority band in updateFetch.
- // Defaults true so unmeasured textures (fresh objects, no-face users) are
- // never starved.
- bool mOnScreen = true;
-
- // How far out of frustum the texture's least-out-of-view use sits, as a
- // fraction of screen size (0 = on screen). Drives the frustum-allowance
- // falloff in computeDesiredDiscard.
- F32 mFrustumOverflow = 0.f;
-
- // Last frame this texture was out of frustum (mFrustumOverflow > 0). The
- // GC in computeDesiredDiscard gives re-entering content one grace window to
- // be drawn and re-stamp mLastBindFrame before its staleness is judged.
- mutable U32 mLastOffScreenFrame = 0;
-
- // Membership flag for LLViewerTextureList::mFastFetchList (dedup).
- bool mInFastFetchList = 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 ;
@@ -254,29 +222,17 @@ public:
static S32 sRawCount;
static S32 sAuxCount;
static LLFrameTimer sEvaluationTimer;
-
- // The global max pixel:texel ratio (texels per screen pixel, the "R" in 1:R).
- // The watermark controller in updateClass walks it between TexturePixelToTexelRatio
- // and 0 as VRAM pressure changes; lower means coarser desired discards.
- static F32 sPixelToTexelRatio;
-
- // Frame index the GC was last suspended (kept current while backgrounded).
- // The foreground GC only runs once getFrameCount() is a grace window past
- // this, so content can re-stamp after an alt-tab before anything is collected.
- static U32 sGCSuspendedFrame;
-
+ static F32 sDesiredDiscardBias;
+ 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,
@@ -322,15 +278,26 @@ 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)
+public:
+
+ struct Compare
{
- return boost_level == BOOST_AVATAR
- || boost_level == BOOST_AVATAR_BAKED
- || boost_level == BOOST_AVATAR_SELF
- || boost_level == BOOST_AVATAR_BAKED_SELF;
- }
+ // lhs < rhs
+ bool operator()(const LLPointer<LLViewerFetchedTexture> &lhs, const LLPointer<LLViewerFetchedTexture> &rhs) const
+ {
+ const LLViewerFetchedTexture* lhsp = (const LLViewerFetchedTexture*)lhs;
+ const LLViewerFetchedTexture* rhsp = (const LLViewerFetchedTexture*)rhs;
+
+ // greater priority is "less"
+ const F32 lpriority = lhsp->mMaxVirtualSize;
+ const F32 rpriority = rhsp->mMaxVirtualSize;
+ if (lpriority > rpriority) // higher priority
+ return true;
+ if (lpriority < rpriority)
+ return false;
+ return lhsp < rhsp;
+ }
+ };
public:
/*virtual*/ S8 getType() const override;
@@ -371,7 +338,7 @@ public:
void updateVirtualSize() ;
- S32 getDesiredDiscardLevel() const { return mDesiredDiscardLevel; }
+ S32 getDesiredDiscardLevel() { return mDesiredDiscardLevel; }
void setMinDiscardLevel(S32 discard) { mMinDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel,(S8)discard); }
void setBoostLevel(S32 level) override;
@@ -493,12 +460,6 @@ 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
@@ -578,21 +539,12 @@ public:
S8 getType() const override;
// Process image stats to determine priority/quality requirements.
void processTextureStats() override;
+ bool isUpdateFrozen() ;
bool scaleDown() override;
private:
void init(bool firstinit) ;
-
- // The whole streaming pipeline: desired discard from the pixel:texel ratio.
- // For each channel bucket the texture is used in, the most-demanding
- // (largest coverage) face sets that bucket's requirement at
- // floor(log4(texels / (R_global * channelRatio[b] * coverage))); the sharpest
- // requirement across buckets wins (one GL image serves all its channels).
- // A per-mip hysteresis dead-band against the current discard level prevents
- // fetch/scaleDown thrash. avatar_bake textures use the configured max ratio
- // instead of the pressure-driven sPixelToTexelRatio (anti cloud-bug).
- S32 computeDesiredDiscard(S32 dim_max_i, bool avatar_bake) const;
};
//
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index ad05a0273b..e4fd947892 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -47,7 +47,6 @@
#include "message.h"
#include "lldrawpoolbump.h" // to init bumpmap images
-#include "llagentcamera.h"
#include "lltexturecache.h"
#include "lltexturefetch.h"
#include "llviewercontrol.h"
@@ -62,11 +61,6 @@
#include "lltracerecording.h"
#include "llviewerdisplay.h"
#include "llviewerwindow.h"
-#include "llsurface.h"
-#include "llvoavatarself.h"
-#include "lldrawable.h"
-#include "llvovolume.h"
-#include "llviewertextureanim.h"
#include "llprogressview.h"
////////////////////////////////////////////////////////////////////////////
@@ -99,20 +93,6 @@ LLTextureKey::LLTextureKey(LLUUID id, ETexListType tex_type)
///////////////////////////////////////////////////////////////////////////////
-// eTexIndex -> TextureChannel* index (0=Normal, 1=BaseColor, 2=Specular,
-// 3=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)
@@ -378,12 +358,6 @@ void LLViewerTextureList::shutdown()
}
mFastCacheList.clear();
- for (auto& img : mFastFetchList)
- {
- img->mInFastFetchList = false;
- }
- mFastFetchList.clear();
-
mUUIDMap.clear();
mImageList.clear();
@@ -877,36 +851,6 @@ void LLViewerTextureList::updateImages(F32 max_time)
remaining_time -= updateImagesFetchTextures(remaining_time);
remaining_time = llmax(remaining_time, min_time);
- // Fast pump: advance every in-flight fetch each frame so results are
- // collected and creates scheduled the frame they're ready, instead of
- // one state transition per round-robin visit. Cheap - no face scans -
- // and bounded by the fetch worker's own concurrency.
- LLTimer fast_fetch_timer;
- S32 min_count = 32;
- for (size_t i = 0; i < mFastFetchList.size(); )
- {
- LLViewerFetchedTexture* imagep = mFastFetchList[i];
- if (imagep->getNumRefs() > 1)
- {
- imagep->updateFetch();
- }
- if (imagep->getNumRefs() <= 1 || (!imagep->isFetching() && !imagep->hasFetcher()))
- {
- imagep->mInFastFetchList = false;
- mFastFetchList[i] = mFastFetchList.back();
- mFastFetchList.pop_back();
- }
- else
- {
- ++i;
- }
-
- if (fast_fetch_timer.getElapsedTimeF32() > remaining_time && --min_count <= 0)
- {
- break;
- }
- }
-
//handle results from decode threads
updateImagesCreateTextures(remaining_time);
@@ -951,383 +895,144 @@ void LLViewerTextureList::clearFetchingRequests()
extern bool gCubeSnapshot;
-// Refresh a face's cached per-channel streaming coverage (face->mStreamVSize).
-// This is the most-demanding-point measurement plus each channel's own UV
-// repeat source, computed ONCE per face per update cadence and shared by every
-// texture registered on the face. Doing the material/transform pointer chases
-// per texture visit instead made updateImageDecodePriority several times more
-// expensive per face than develop's, and since the round-robin runs in a fixed
-// per-frame time slice, that directly cut how many textures advance their
-// load state each frame - the whole pipeline paced slower.
-static void update_face_stream_vsize(LLFace* face)
-{
- // Bounds on the per-face UV repeat-area divisor (mined from the old
- // getTextureVirtualSize texel_area clamp [1/64, 128]): atlas/crop boost
- // capped at 64x (3 mips finer), tiling penalty at 128x (3.5 mips
- // coarser) so pathological UV scales can't explode either direction.
- constexpr F32 MIN_REPEAT_AREA = 1.f / 64.f;
- constexpr F32 MAX_REPEAT_AREA = 128.f;
-
- LLViewerObject* objp = face->getViewerObject();
-
- // Most-demanding-point measurement: the spec is that the LOWEST pixel:texel
- // ratio governs, so pixel density is evaluated at the face's NEAREST point
- // and applied to the face's true world area. A whole-face average
- // (bounding-disc pixel area) under-resolves perspective surfaces: on a
- // floor, the tile at your feet covers far more screen than the average
- // tile, and the GPU samples fine mips right there.
- const LLVector4a* ext = face->isState(LLFace::RIGGED) ? face->mRiggedExtents : face->mExtents;
- LLVector4a diag;
- diag.setSub(ext[1], ext[0]);
- // World area of the face ~ product of the two largest AABB dims (max
- // pairwise product; robust for flat faces).
- F32 dx = diag[0], dy = diag[1], dz = diag[2];
- F32 area_world = llmax(dx * dy, llmax(dx * dz, dy * dz));
- // Pixels per meter at the nearest point. Distance floored: nearer than
- // this the screen clamp below governs anyway.
- F32 dist = llmax(face->mDistanceToCamera, 0.5f);
- F32 ppm = LLDrawable::sCurPixelAngle / dist;
- F32 face_px = area_world * ppm * ppm;
- if (face_px <= 0.f)
- {
- // Degenerate extents: the face hasn't been through a geometry build
- // yet (or a rigged face has no rigged extents) - it isn't renderable,
- // so it must not be measured. Zero marks "skip": an invented
- // placeholder value would become the texture's least-demanding "use"
- // and, under TextureDownrezCoverageBias, drag the whole texture to
- // its deepest mip (and it poisoned BP and PBR asymmetrically, since
- // the two register faces at different points in the geometry
- // lifecycle).
- for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
- {
- face->mStreamVSize[ch] = 0.f;
- }
- return;
- }
-
- S32 te_offset = face->getTEOffset(); // offset is -1 if not inited
- const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset);
-
- // Shared, channel-independent chases - hoisted out of the channel loop.
- const LLGLTFMaterial* gltf_mat = te ? te->getGLTFRenderMaterial() : nullptr;
- const LLMaterial* mat = te ? te->getMaterialParams().get() : nullptr;
-
- // Continuously-animated scale (llSetTextureAnim SCALE) bypasses both
- // static sources via mTextureMatrix - the live animated values win.
- bool anim_scale = false;
- F32 anim_ss = 0.f, anim_st = 0.f;
- if (te)
- {
- if (LLVOVolume* vvo = face->getDrawable() ? face->getDrawable()->getVOVolume() : nullptr)
- {
- LLViewerTextureAnim* anim = vvo->mTextureAnimp;
- if (anim && (anim->mMode & LLTextureAnim::ON) && (anim->mMode & LLTextureAnim::SCALE)
- && (anim->mFace < 0 || anim->mFace == te_offset))
- {
- anim_scale = true;
- anim_ss = anim->mScaleS;
- anim_st = anim->mScaleT;
- }
- }
- }
-
- // Mesh atlas sub-rect: a face whose intrinsic UVs span only part of
- // [0,1]^2 shows that fraction of the image. Applies identically to all
- // channels - the per-channel transforms stack on the raw face UVs.
- F32 span = 1.f;
- if (te)
- {
- if (LLVolume* vol = objp->getVolume())
- {
- if (te_offset >= 0 && te_offset < vol->getNumVolumeFaces())
- {
- const LLVolumeFace& vf = vol->getVolumeFace(te_offset);
- F32 s = fabsf((vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0])
- * (vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1]));
- if (s > 0.f)
- {
- span = s;
- }
- }
- }
- }
-
- // Avatar bonus: worn attachments get a coverage multiplier - avatars are
- // what people look at, and rigged extents make attachment coverage
- // measurement unreliable anyway. Multiplicative, not a slam.
- static LLCachedControl<F32> avatar_boost(gSavedSettings, "TextureAvatarBoost", 4.f);
- const F32 boost = objp->isAttachment() ? llmax((F32)avatar_boost, 1.f) : 1.f;
-
- for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
- {
- // Effective UV repeat AREA: the tiling term of texels-drawn-per-
- // screen-pixel. More tiling => each tile smaller on screen => coarser
- // mips suffice (penalty). Repeats < 1 (atlas/crop) => whole-image
- // residency for a sub-rect legitimately demands more than its screen
- // coverage (boost).
- F32 repeats = 1.f;
- if (te)
- {
- // UV scale source: every channel reads the repeat values ITS
- // renderer actually applies. diffuse -> TE scale; Blinn
- // normal/spec -> LLMaterial per-map repeats; PBR channels -> KHR
- // texture_transform scale. Fallback is the TE scale - never a
- // silent hardcoded 1.
- F32 scale_s = te->getScaleS();
- F32 scale_t = te->getScaleT();
- if (ch >= LLRender::BASECOLOR_MAP)
- {
- // LLRender channel -> LLGLTFMaterial::TextureInfo
- static const S32 gltf_info[4] = {
- LLGLTFMaterial::GLTF_TEXTURE_INFO_BASE_COLOR, // BASECOLOR_MAP (3)
- LLGLTFMaterial::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS, // METALLIC_ROUGHNESS_MAP (4)
- LLGLTFMaterial::GLTF_TEXTURE_INFO_NORMAL, // GLTF_NORMAL_MAP (5)
- LLGLTFMaterial::GLTF_TEXTURE_INFO_EMISSIVE, // EMISSIVE_MAP (6)
- };
- if (gltf_mat)
- {
- const LLVector2& s = gltf_mat->mTextureTransform[gltf_info[ch - LLRender::BASECOLOR_MAP]].mScale;
- scale_s = s.mV[0];
- scale_t = s.mV[1];
- }
- }
- else if (ch == LLRender::NORMAL_MAP || ch == LLRender::SPECULAR_MAP)
- {
- // Blinn-Phong normal/specular maps carry their own repeats in
- // LLMaterial - the renderer builds their texture matrices
- // from these, NOT from the TE's diffuse scale.
- if (mat)
- {
- if (ch == LLRender::NORMAL_MAP)
- {
- mat->getNormalRepeat(scale_s, scale_t);
- }
- else
- {
- mat->getSpecularRepeat(scale_s, scale_t);
- }
- }
- }
-
- if (anim_scale)
- {
- scale_s = anim_ss;
- scale_t = anim_st;
- }
-
- repeats = fabsf(scale_s * scale_t) * span;
- }
-
- repeats = llclamp(repeats, MIN_REPEAT_AREA, MAX_REPEAT_AREA);
-
- // Apply the two sides of the repeat term in the right order relative
- // to the screen clamp: tiling (repeats > 1) divides the nearest-point
- // footprint BEFORE the clamp (one tile can't draw more pixels than
- // the screen); atlas/crop (repeats < 1) boosts AFTER it (whole-image
- // residency for a crop legitimately demands more than its screen
- // coverage).
- F32 tiling = llmax(repeats, 1.f);
- F32 crop = llmin(repeats, 1.f);
- face->mStreamVSize[ch] = llmin(face_px / tiling, LLViewerTexture::sWindowPixelArea) / crop * boost;
- }
-}
-
void LLViewerTextureList::updateImageDecodePriority(LLViewerFetchedTexture* imagep, bool flush_images)
{
llassert(!gCubeSnapshot);
- // Refresh spotlight priorities first: light projector textures register as
- // LIGHT_TEX volumes (no faces), and both their fetch priority
- // (addTextureStats inside updateSpotLightPriority) and their coverage
- // (folded into the block below) derive from mSpotLightPriority.
- for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi)
- {
- LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi];
- volume->updateSpotLightPriority();
- }
+ constexpr F32 BIAS_TRS_OUT_OF_SCREEN = 1.5f;
+ constexpr F32 BIAS_TRS_ON_SCREEN = 1.f;
if (imagep->getBoostLevel() < LLViewerFetchedTexture::BOOST_HIGH) // don't bother checking face list for boosted textures
{
- // Per priority bucket (0=Normal, 1=BaseColor, 2=Specular, 3=Emissive):
- // the HIGHEST per-face effective coverage (= the lowest texels-per-pixel
- // use, the most demanding variant - drives desired discard) and the
- // LOWEST positive coverage (= the highest texels-per-pixel use, the most
- // oversampled variant - available for downrez-bias policy). The overall
- // max is the fetch priority (raw - no bias). A bucket with faces but
- // zero coverage (all off-screen) publishes 0, which
- // computeDesiredDiscard reads as "coarsest mip".
- F32 channel_coverage[4] = { 0.f, 0.f, 0.f, 0.f };
- F32 channel_coverage_min[4] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
- bool bucket_used[4] = { false, false, false, false };
- F32 max_coverage = 0.f;
- bool on_screen = false; // any face's projected disc overlaps the screen
- bool any_face = false;
- F32 min_overflow = FLT_MAX; // least out-of-frustum use across faces
+ static LLCachedControl<F32> texture_scale_min(gSavedSettings, "TextureScaleMinAreaFactor", 0.0095f);
+ static LLCachedControl<F32> texture_scale_max(gSavedSettings, "TextureScaleMaxAreaFactor", 25.f);
+ F32 max_vsize = 0.f;
+ bool on_screen = false;
U32 face_count = 0;
- const U32 max_faces_to_check = 1024;
+ U32 max_faces_to_check = 1024;
+
+ // get adjusted bias based on image resolution
+ LLImageGL* img = imagep->getGLTexture();
+ F32 max_discard = F32(img ? img->getMaxDiscardLevel() : MAX_DISCARD_LEVEL);
+ F32 bias = llclamp(max_discard - 2.f, 1.f, LLViewerTexture::sDesiredDiscardBias);
+
+ // convert bias into a vsize scaler
+ bias = (F32) llroundf(powf(4, bias - 1.f));
- // Cheap first pass: which buckets is this texture used in, and how many
- // faces total. No per-face geometry work.
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
{
- U32 n = imagep->getNumFaces(i);
- face_count += n;
- if (n > 0)
- {
- S32 b = sChannelToPriority[i];
- if (b >= 0 && b < 4) bucket_used[b] = true;
- }
- }
+ face_count += imagep->getNumFaces(i);
+ S32 faces_to_check = (face_count > max_faces_to_check) ? 0 : imagep->getNumFaces(i);
- if (face_count > max_faces_to_check)
- {
- // Used in so many places that scanning the face list isn't worth it
- // (and isn't time-sliced) - treat as full-screen so it loads sharp.
- for (S32 b = 0; b < 4; ++b)
- {
- if (bucket_used[b])
- {
- channel_coverage[b] = (F32)MAX_IMAGE_AREA;
- channel_coverage_min[b] = (F32)MAX_IMAGE_AREA;
- }
- }
- max_coverage = (F32)MAX_IMAGE_AREA;
- }
- else
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
+ for (S32 fi = 0; fi < faces_to_check; ++fi)
{
- const S32 bucket = sChannelToPriority[i];
- const U32 num_faces = imagep->getNumFaces(i);
- for (U32 fi = 0; fi < num_faces; ++fi)
- {
- LLFace* face = (*(imagep->getFaceList(i)))[fi];
- if (!face || !face->getViewerObject())
- {
- continue;
- }
+ LLFace* face = (*(imagep->getFaceList(i)))[fi];
+ if (face && face->getViewerObject())
+ {
F32 radius;
F32 cos_angle_to_view_dir;
+
if ((gFrameCount - face->mLastTextureUpdate) > 10)
- { // refresh the face's geometry + cached coverage at most once every
- // 10 frames; every texture/channel sharing this face (GLTF and
- // Blinn-Phong materials) reuses the cache instead of redoing the
- // measurement. (calcPixelArea maintains face->mInFrustum itself.)
- face->calcPixelArea(cos_angle_to_view_dir, radius);
- update_face_stream_vsize(face);
+ { // only call calcPixelArea at most once every 10 frames for a given face
+ // this helps eliminate redundant calls to calcPixelArea for faces that have multiple textures
+ // assigned to them, such as is the case with GLTF materials or Blinn-Phong materials
+ face->mInFrustum = face->calcPixelArea(cos_angle_to_view_dir, radius);
face->mLastTextureUpdate = gFrameCount;
}
- // Cached measurement - see update_face_stream_vsize above.
- // Zero = degenerate extents / not yet through a geometry
- // build: not renderable, must not be measured (a
- // placeholder value would poison the per-bucket MIN bound
- // and drag the texture to its deepest mip).
- F32 vsize = face->mStreamVSize[i];
- if (vsize <= 0.f)
+ F32 vsize = face->getPixelArea();
+
+ on_screen |= face->mInFrustum;
+
+ // Scale desired texture resolution higher or lower depending on texture scale
+ //
+ // Minimum usage examples: a 1024x1024 texture with aplhabet (texture atlas),
+ // runing string shows one letter at a time. If texture has ten 100px symbols
+ // per side, minimal scale is (100/1024)^2 = 0.0095
+ //
+ // Maximum usage examples: huge chunk of terrain repeats texture
+ // TODO: make this work with the GLTF texture transforms
+ S32 te_offset = face->getTEOffset(); // offset is -1 if not inited
+ LLViewerObject* objp = face->getViewerObject();
+ const LLTextureEntry* te = (te_offset < 0 || te_offset >= objp->getNumTEs()) ? nullptr : objp->getTE(te_offset);
+ F32 min_scale = te ? llmin(fabsf(te->getScaleS()), fabsf(te->getScaleT())) : 1.f;
+ min_scale = llclamp(min_scale * min_scale, texture_scale_min(), texture_scale_max());
+ vsize /= min_scale;
+
+ // 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)
+ {
+ vsize /= bias;
+ }
+
+ // boost resolution of textures that are important to the camera
+ if (face->mInFrustum)
{
- continue;
+ static LLCachedControl<F32> texture_camera_boost(gSavedSettings, "TextureCameraBoost", 8.f);
+ vsize *= llmax(face->mImportanceToCamera*texture_camera_boost, 1.f);
}
- any_face = true;
- on_screen = on_screen || face->mInFrustum;
- min_overflow = llmin(min_overflow, face->mFrustumOverflow);
+ max_vsize = llmax(max_vsize, vsize);
- if (bucket >= 0 && bucket < 4)
+ // addTextureStats limits size to sMaxVirtualSize
+ if (max_vsize >= LLViewerFetchedTexture::sMaxVirtualSize
+ && (on_screen || LLViewerTexture::sDesiredDiscardBias <= BIAS_TRS_ON_SCREEN))
{
- channel_coverage[bucket] = llmax(channel_coverage[bucket], vsize);
- if (vsize > 0.f)
- {
- channel_coverage_min[bucket] = llmin(channel_coverage_min[bucket], vsize);
- }
+ break;
}
- max_coverage = llmax(max_coverage, vsize);
}
}
- }
- // Terrain detail textures register no faces (LLVOSurfacePatch
- // addFace(NULL)); synthesize coverage from the nearest visible patch so
- // they degrade with distance like everything else.
- if (face_count == 0 && imagep->getBoostLevel() == LLGLTexture::BOOST_TERRAIN)
- {
- F32 nearest = LLSurface::sNearestVisiblePatchDistance;
- F32 draw_distance = llmax(gAgentCamera.mDrawDistance, 1.f);
- F32 near_frac = (nearest < FLT_MAX) ? llclampf(1.f - nearest / draw_distance) : 0.f;
- // Floor so terrain never collapses to nothing at draw distance.
- F32 cov = LLViewerTexture::sWindowPixelArea * llmax(near_frac, 0.05f);
- channel_coverage[1] = cov; // terrain detail maps are diffuse / base color
- channel_coverage_min[1] = cov;
- max_coverage = cov;
- }
- // Baked avatar textures render on system-avatar body meshes whose
- // joint meshes never register faces with the texture list
- // (LLAvatarJointMesh::setTexture just stores the pointer).
- // LLVOAvatar::updateTextures feeds their on-screen pixel area through
- // addTextureStats every frame - use that as BaseColor coverage, with
- // the same avatar bonus attachments get above.
- else if (face_count == 0
- && (imagep->getFTType() == FTT_SERVER_BAKE || imagep->getFTType() == FTT_HOST_BAKE))
- {
- static LLCachedControl<F32> avatar_boost(gSavedSettings, "TextureAvatarBoost", 4.f);
- F32 cov = llmin(imagep->getMaxVirtualSize(), LLViewerTexture::sWindowPixelArea)
- * llmax((F32)avatar_boost, 1.f);
- if (cov > 0.f)
+ if (max_vsize >= LLViewerFetchedTexture::sMaxVirtualSize
+ && (on_screen || LLViewerTexture::sDesiredDiscardBias <= BIAS_TRS_ON_SCREEN))
{
- channel_coverage[1] = cov;
- channel_coverage_min[1] = cov;
- max_coverage = cov;
+ break;
}
}
- // Light projector textures register as LIGHT_TEX volumes, not faces.
- // mSpotLightPriority (refreshed above) is already a screen-pixel-area
- // estimate of the lit radius - fold it in as BaseColor coverage so
- // projectors stream view-dependently like everything else.
- for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi)
- {
- LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi];
- F32 cov = llmin(volume->getSpotLightPriority(), LLViewerTexture::sWindowPixelArea);
- if (cov > 0.f)
- {
- channel_coverage[1] = llmax(channel_coverage[1], cov);
- channel_coverage_min[1] = llmin(channel_coverage_min[1], cov);
- max_coverage = llmax(max_coverage, cov);
- }
+ if (face_count > max_faces_to_check)
+ { // 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;
}
- imagep->addTextureStats(max_coverage);
+ 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
- // Publish per-bucket coverage bounds for
- // LLViewerLODTexture::computeDesiredDiscard.
- for (S32 b = 0; b < 4; ++b)
- {
- imagep->mChannelCoverage[b] = channel_coverage[b];
- imagep->mChannelCoverageMin[b] = (channel_coverage_min[b] == FLT_MAX) ? 0.f : channel_coverage_min[b];
+ if (LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_OUT_OF_SCREEN ||
+ (!on_screen && LLViewerTexture::sDesiredDiscardBias > BIAS_TRS_ON_SCREEN))
+ {
+ imagep->mMaxVirtualSize = 0.f;
+ }
}
- // Fetch admission signal: false only when faces were actually scanned and
- // every one projects off screen. Textures with no scannable faces (bakes,
- // spotlights, the >1024-face boost path, not-yet-built geometry) stay
- // eligible - blocking them is what stalls load-in.
- imagep->mOnScreen = on_screen || !any_face;
- // Least out-of-frustum use governs the allowance falloff; unknown = 0
- // (no penalty), same reasoning as mOnScreen.
- imagep->mFrustumOverflow = any_face ? min_overflow : 0.f;
+ imagep->addTextureStats(max_vsize);
}
#if 0
- imagep->setDebugText(llformat("%d/%d -- %d/%d",
+ imagep->setDebugText(llformat("%d/%d - %d/%d -- %d/%d",
+ (S32)sqrtf(max_vsize),
+ (S32)sqrtf(imagep->mMaxVirtualSize),
imagep->getDiscardLevel(),
imagep->getDesiredDiscardLevel(),
imagep->getWidth(),
imagep->getFullWidth()));
#endif
+ // make sure to addTextureStats for any spotlights that are using this texture
+ for (S32 vi = 0; vi < imagep->getNumVolumes(LLRender::LIGHT_TEX); ++vi)
+ {
+ LLVOVolume* volume = (*imagep->getVolumeList(LLRender::LIGHT_TEX))[vi];
+ volume->updateSpotLightPriority();
+ }
+
F32 max_inactive_time = 20.f; // inactive time before deleting saved raw image
S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, and 1 for "entries" in updateImagesFetchTextures
@@ -1417,7 +1122,8 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time)
imagep->postCreateTexture();
imagep->mCreatePending = false;
- if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel())
+ if (imagep->hasGLTexture() && imagep->getDiscardLevel() < imagep->getDesiredDiscardLevel() &&
+ (imagep->getDesiredDiscardLevel() <= MAX_DISCARD_LEVEL))
{
// 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,
@@ -1442,16 +1148,13 @@ 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
- // Drain rate scales with both pending creates and the downscale
- // queue itself. Without the queue term, a backlog of evictions
- // could only drain 5/frame regardless of size, and the system
- // can't actually free VRAM fast enough under pressure.
- S32 min_count = (S32)mCreateTextureList.size() / 20
- + (S32)mDownScaleQueue.size() / 5
- + 5;
+ // do at least 5 and make sure we don't get too far behind even if it violates
+ // the time limit. If we don't downscale quickly the viewer will hit swap and may
+ // freeze.
+ S32 min_count = (S32)mCreateTextureList.size() / 20 + 5;
create_timer.reset();
while (!mDownScaleQueue.empty())
@@ -1553,6 +1256,18 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
//update MIN_UPDATE_COUNT or 5% of other textures, whichever is greater
update_count = llmax((U32) MIN_UPDATE_COUNT, (U32) mUUIDMap.size()/20);
+ if (LLViewerTexture::sDesiredDiscardBias > 1.f
+ && LLViewerTexture::sBiasTexturesUpdated < (U32)mUUIDMap.size())
+ {
+ // We are over memory target. Bias affects discard rates, so update
+ // existing textures agresively to free memory faster.
+ update_count = (S32)(update_count * LLViewerTexture::sDesiredDiscardBias);
+
+ // This isn't particularly precise and can overshoot, but it doesn't need
+ // to be, just making sure it did a full circle and doesn't get stuck updating
+ // at bias = 4 with 4 times the rate permanently.
+ LLViewerTexture::sBiasTexturesUpdated += update_count;
+ }
update_count = llmin(update_count, (U32) mUUIDMap.size());
{ // copy entries out of UUID map to avoid iterator invalidation from deletion inside updateImageDecodeProiroty or updateFetch below
@@ -1586,18 +1301,6 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time)
{
updateImageDecodePriority(imagep);
imagep->updateFetch();
-
- // Fast-pump membership: textures with an active fetch get
- // updateFetch every frame (in updateImages) instead of waiting
- // ~a sweep period per state transition - that wait, times the
- // 2-4 transitions a load needs, was the measured throughput
- // ceiling. Purely additive: the sweep still pumps everything,
- // so fetches started by any other path can never strand.
- if ((imagep->isFetching() || imagep->hasFetcher()) && !imagep->mInFastFetchList)
- {
- imagep->mInFastFetchList = true;
- mFastFetchList.push_back(imagep);
- }
}
if (timer.getElapsedTimeF32() > max_time)
diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h
index 7004238995..7c7112f4cf 100644
--- a/indra/newview/llviewertexturelist.h
+++ b/indra/newview/llviewertexturelist.h
@@ -30,11 +30,9 @@
#include "lluuid.h"
//#include "message.h"
#include "llgl.h"
-#include "llrender.h"
#include "llviewertexture.h"
#include "llui.h"
#include <list>
-#include <map>
#include <unordered_set>
#include "lluiimage.h"
@@ -94,10 +92,6 @@ class LLViewerTextureList
friend class LLLocalBitmap;
public:
- // eTexIndex -> TextureChannel* index (0=Normal, 1=BaseColor,
- // 2=Specular, 3=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,
@@ -226,10 +220,6 @@ public:
image_list_t mCallbackList;
image_list_t mFastCacheList;
- // In-flight fetches pumped every frame (additive to the round-robin
- // sweep, which remains the universal pump). See updateImages.
- std::vector<LLPointer<LLViewerFetchedTexture> > mFastFetchList;
-
bool mForceResetTextureStats;
// to make "for (auto& imagep : gTextureList)" work
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index e2601e6465..dea96e2012 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -5597,13 +5597,7 @@ bool LLViewerWindow::cubeSnapshot(const LLVector3& origin, LLCubeMapArray* cubea
// actually render the scene
gCubeSnapshot = true;
- {
- // Probe binds aren't visibility - otherwise every probe slice re-stamps
- // behind-camera textures and cycles them evict->refetch. RAII so the
- // nested shadow pass in display_cube_face doesn't re-enable stamping.
- LLImageGLStampBypass stamp_bypass;
- display_cube_face();
- }
+ display_cube_face();
gCubeSnapshot = false;
}
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
index eb0261b5e5..3441e25c6a 100644
--- a/indra/newview/llvlcomposition.cpp
+++ b/indra/newview/llvlcomposition.cpp
@@ -173,10 +173,7 @@ LLPointer<LLViewerFetchedTexture> fetch_terrain_texture(const LLUUID& id)
return nullptr;
}
- // 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);
+ LLPointer<LLViewerFetchedTexture> tex = LLViewerTextureManager::getFetchedTexture(id);
return tex;
}
@@ -346,9 +343,18 @@ 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;
}
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
index d57456aa5a..3b41ccb6fc 100644
--- a/indra/newview/llvovolume.cpp
+++ b/indra/newview/llvovolume.cpp
@@ -878,15 +878,11 @@ void LLVOVolume::updateTextureVirtualSize(bool forced)
LLViewerFetchedTexture* img = LLViewerTextureManager::staticCastToFetchedTexture(imagep) ;
if(img)
{
- // cur:desired:width then per-bucket coverage bounds
- // (N/BC/S/E, sqrt so values read as pixel dimensions,
- // max~min) - the exact inputs computeDesiredDiscard sees.
- debug_text << img->getDiscardLevel() << ":" << img->getDesiredDiscardLevel() << ":" << img->getWidth()
- << " N" << (S32)sqrtf(img->getChannelCoverage(0)) << "~" << (S32)sqrtf(img->getChannelCoverageMin(0))
- << " BC" << (S32)sqrtf(img->getChannelCoverage(1)) << "~" << (S32)sqrtf(img->getChannelCoverageMin(1))
- << " S" << (S32)sqrtf(img->getChannelCoverage(2)) << "~" << (S32)sqrtf(img->getChannelCoverageMin(2))
- << " E" << (S32)sqrtf(img->getChannelCoverage(3)) << "~" << (S32)sqrtf(img->getChannelCoverageMin(3))
- << "\n";
+ debug_text << img->getDiscardLevel() << ":" << img->getDesiredDiscardLevel() << ":" << img->getWidth() << ":" << (S32) sqrtf(vsize) << ":" << (S32) sqrtf(img->getMaxVirtualSize()) << "\n";
+ /*F32 pri = img->getDecodePriority();
+ pri = llmax(pri, 0.0f);
+ if (pri < min_vsize) min_vsize = pri;
+ if (pri > max_vsize) max_vsize = pri;*/
}
}
else if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_AREA))
@@ -932,7 +928,7 @@ void LLVOVolume::updateTextureVirtualSize(bool forced)
{
LLLightImageParams* params = getLightImageParams();
LLUUID id = params->getLightTexture();
- mLightTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ mLightTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE);
if (mLightTexture.notNull())
{
F32 rad = getLightRadius();
@@ -1801,41 +1797,6 @@ void LLVOVolume::regenFaces()
facep->setNormalMap(getTENormalMap(i));
facep->setSpecularMap(getTESpecularMap(i));
}
-
- // Register PBR channel textures HERE, at geometry build, exactly when
- // the Blinn textures above register - mirrored from rebuildGeom
- // (which still re-runs this when the material resolves later).
- // Without this, PBR textures had zero registered faces until the
- // spatial group's budget-throttled rebuildGeom ran, so the streaming
- // math read "not measured -> deepest mip" for PBR content while
- // identical Blinn content on the same geometry measured immediately.
- {
- const LLTextureEntry* te = facep->getTextureEntry();
- LLFetchedGLTFMaterial* gltf_mat = te ? (LLFetchedGLTFMaterial*)te->getGLTFRenderMaterial() : nullptr;
- if (gltf_mat)
- {
- if (!facep->hasMedia())
- {
- facep->setTexture(LLRender::DIFFUSE_MAP, nullptr);
- }
- facep->setTexture(LLRender::NORMAL_MAP, nullptr);
- facep->setTexture(LLRender::SPECULAR_MAP, nullptr);
- facep->setTexture(LLRender::BASECOLOR_MAP, gltf_mat->mBaseColorTexture);
- facep->setTexture(LLRender::GLTF_NORMAL_MAP, gltf_mat->mNormalTexture);
- facep->setTexture(LLRender::METALLIC_ROUGHNESS_MAP, gltf_mat->mMetallicRoughnessTexture);
- facep->setTexture(LLRender::EMISSIVE_MAP, gltf_mat->mEmissiveTexture);
- }
- else
- {
- // No (or no longer a) PBR material: clear any stale GLTF
- // channel registrations so a removed material's textures
- // stop accruing phantom coverage from this face.
- facep->setTexture(LLRender::BASECOLOR_MAP, nullptr);
- facep->setTexture(LLRender::GLTF_NORMAL_MAP, nullptr);
- facep->setTexture(LLRender::METALLIC_ROUGHNESS_MAP, nullptr);
- facep->setTexture(LLRender::EMISSIVE_MAP, nullptr);
- }
- }
facep->setViewerObject(this);
// If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face.
@@ -3406,7 +3367,7 @@ LLViewerTexture* LLVOVolume::getLightTexture()
{
if (mLightTexture.isNull() || id != mLightTexture->getID())
{
- mLightTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE);
+ mLightTexture = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, true, LLGLTexture::BOOST_NONE);
}
}
else
@@ -5890,16 +5851,6 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group)
facep->setTexture(LLRender::METALLIC_ROUGHNESS_MAP, gltf_mat->mMetallicRoughnessTexture);
facep->setTexture(LLRender::EMISSIVE_MAP, gltf_mat->mEmissiveTexture);
}
- else
- {
- // Face is not (or no longer) PBR: clear any stale GLTF
- // channel registrations, or a removed material's textures
- // keep accruing phantom coverage from this face forever.
- facep->setTexture(LLRender::BASECOLOR_MAP, nullptr);
- facep->setTexture(LLRender::GLTF_NORMAL_MAP, nullptr);
- facep->setTexture(LLRender::METALLIC_ROUGHNESS_MAP, nullptr);
- facep->setTexture(LLRender::EMISSIVE_MAP, nullptr);
- }
//ALWAYS null out vertex buffer on rebuild -- if the face lands in a render
// batch, it will recover its vertex buffer reference from the spatial group
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index f18ec727df..4ef88f5deb 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -9461,9 +9461,6 @@ void LLPipeline::renderShadow(const glm::mat4& view, const glm::mat4& proj, LLCa
LL_PROFILE_GPU_ZONE("renderShadow");
LLPipeline::sShadowRender = true;
- // Shadow binds aren't visibility. RAII (not a hardcoded restore) so a shadow
- // pass nested in a probe render doesn't re-enable stamping for the rest of it.
- LLImageGLStampBypass stamp_bypass;
// disable occlusion culling during shadow render
U32 saved_occlusion = sUseOcclusion;
@@ -10848,9 +10845,6 @@ void LLPipeline::generateImpostor(LLVOAvatar* avatar, bool preview_avatar, bool
sShadowRender = true;
sImpostorRender = true;
- // Impostor binds aren't visibility. RAII so the nested shadow pass can't
- // re-enable stamping mid-render.
- LLImageGLStampBypass stamp_bypass;
LLViewerCamera* viewer_camera = LLViewerCamera::getInstance();
diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
index 3043b17589..99dca7b395 100644
--- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
+++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml
@@ -127,35 +127,31 @@
top_delta="16"
left="30"
width="160"
- name="TextureQualityLabel"
+ name="MaxTextureResolutionLabel"
text_readonly_color="LabelDisabledColor">
- Texture quality:
+ Maximum LOD resolution:
</text>
<combo_box
- control_name="RenderTextureQuality"
+ control_name="RenderMaxTextureResolution"
height="19"
layout="topleft"
left_pad="10"
top_delta="0"
- name="TextureQuality"
- tool_tip="Controls maximum texture resolution and how aggressively textures stream by channel. Higher quality keeps more detail in memory."
+ name="MaxTextureResolution"
+ tool_tip="Maximum resolution for 'level of detail' textures"
width="90">
<combo_box.item
- label="Low"
- name="Low"
- value="0"/>
- <combo_box.item
- label="Medium"
- name="Medium"
- value="1"/>
+ label="512"
+ name="512"
+ value="512"/>
<combo_box.item
- label="High"
- name="High"
- value="2"/>
+ label="1024"
+ name="1024"
+ value="1024"/>
<combo_box.item
- label="Ultra"
- name="Ultra"
- value="3"/>
+ label="2048"
+ name="2048"
+ value="2048"/>
</combo_box>
<check_box