diff options
Diffstat (limited to 'indra/newview/llviewertexture.cpp')
| -rw-r--r-- | indra/newview/llviewertexture.cpp | 276 |
1 files changed, 204 insertions, 72 deletions
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 0f23596c9a..79439b4d58 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -87,6 +87,8 @@ S32 LLViewerTexture::sRawCount = 0; S32 LLViewerTexture::sAuxCount = 0; LLFrameTimer LLViewerTexture::sEvaluationTimer; F32 LLViewerTexture::sDesiredDiscardBias = 0.f; +F32 LLViewerTexture::sBackgroundFactor = 0.f; +F32 LLViewerTexture::sMemoryPressureFactor = 0.f; U32 LLViewerTexture::sBiasTexturesUpdated = 0; S32 LLViewerTexture::sMaxSculptRez = 128; //max sculpt image size @@ -97,12 +99,12 @@ constexpr S32 DEFAULT_ICON_DIMENSIONS = 32; constexpr S32 DEFAULT_THUMBNAIL_DIMENSIONS = 256; U32 LLViewerTexture::sMinLargeImageSize = 65536; //256 * 256. U32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA; -bool LLViewerTexture::sFreezeImageUpdates = false; F32 LLViewerTexture::sCurrentTime = 0.0f; constexpr F32 MEMORY_CHECK_WAIT_TIME = 1.0f; constexpr F32 MIN_VRAM_BUDGET = 768.f; F32 LLViewerTexture::sFreeVRAMMegabytes = MIN_VRAM_BUDGET; +F32 LLViewerTexture::sWindowPixelArea = 1.f; LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF; @@ -120,7 +122,7 @@ LLLoadedCallbackEntry::LLLoadedCallbackEntry(loaded_callback_func cb, LLViewerFetchedTexture* target, bool pause) : mCallback(cb), - mLastUsedDiscard(MAX_DISCARD_LEVEL+1), + mLastUsedDiscard(S32_MAX), mDesiredDiscard(discard_level), mNeedsImageRaw(need_imageraw), mUserData(userdata), @@ -485,6 +487,13 @@ void LLViewerTexture::updateClass() LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; sCurrentTime = gFrameTimeSeconds; + if (gViewerWindow) + { + F32 w = (F32)gViewerWindow->getWindowWidthRaw(); + F32 h = (F32)gViewerWindow->getWindowHeightRaw(); + sWindowPixelArea = llmax(w * h, 1.f); + } + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); if (tester) { @@ -523,6 +532,28 @@ void LLViewerTexture::updateClass() F32 over_pct = (used - target) / target; + // VRAM-pressure feedback loop with progressive backoff. Ramp starts at + // backoff_start x target, not at the budget cliff. Proportional in both + // directions: ramp = (over-1)(1-factor), decay = factor; converges at + // factor = 1 - 1/over_at_backoff_target. + { + static LLCachedControl<F32> backoff_start(gSavedSettings, "TextureMemoryPressureBackoffStart", 0.85f); + F32 backoff_target = target * llclamp((F32)backoff_start, 0.05f, 1.f); + F32 over = used / llmax(backoff_target, 1.f); + static LLCachedControl<F32> pressure_ramp_rate(gSavedSettings, "TextureMemoryPressureRampRate", 3.0f); + F32 dt = gFrameIntervalSeconds; + if (over > 1.f) + { + sMemoryPressureFactor += + (over - 1.f) * (1.f - sMemoryPressureFactor) * (F32)pressure_ramp_rate * dt; + } + else + { + sMemoryPressureFactor -= sMemoryPressureFactor * (F32)pressure_ramp_rate * dt; + } + sMemoryPressureFactor = llclampf(sMemoryPressureFactor); + } + bool is_sys_low = isSystemMemoryLow(); bool is_low = is_sys_low || over_pct > 0.f; @@ -569,6 +600,10 @@ void LLViewerTexture::updateClass() // don't execute above until the slam to 1.5 has a chance to take effect sEvaluationTimer.reset(); + // Don't decay bias while downscale is still draining - those bytes + // are about to free and the loop would oscillate. + bool eviction_in_flight = !gTextureList.mDownScaleQueue.empty(); + // lower discard bias over time when at least 10% of budget is free constexpr F32 FREE_PERCENTAGE_TRESHOLD = -0.1f; constexpr U32 FREE_SYS_MEM_TRESHOLD = 100; @@ -576,7 +611,8 @@ void LLViewerTexture::updateClass() const S32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory() + FREE_SYS_MEM_TRESHOLD); if (sDesiredDiscardBias > 1.f && over_pct < FREE_PERCENTAGE_TRESHOLD - && getFreeSystemMemory() > MIN_FREE_MAIN_MEMORY) + && getFreeSystemMemory() > MIN_FREE_MAIN_MEMORY + && !eviction_in_flight) { static LLCachedControl<F32> high_mem_discard_decrement(gSavedSettings, "RenderHighMemMinDiscardDecrement", .1f); @@ -622,6 +658,33 @@ void LLViewerTexture::updateClass() } } + // Background-window ramp: 0 -> 1 at rate per second while backgrounded, + // snaps to 0 in foreground. Default 0.011 ~ 90s to saturate. + { + static LLCachedControl<F32> bg_factor_rate(gSavedSettings, "TextureBackgroundFactorRatePerSec", 0.011f); + if (in_background) + { + sBackgroundFactor += (F32)bg_factor_rate * gFrameIntervalSeconds; + sBackgroundFactor = llclampf(sBackgroundFactor); + } + else + { + sBackgroundFactor = 0.f; + } + } + + // Fetch-queue depth as a one-way bias floor (decay path still drops + // bias when the queue drains). Pushes bias up before VRAM overflows + // during teleport/scene-change floods. + if (LLTextureFetch* fetcher = LLAppViewer::getTextureFetch()) + { + S32 pending = fetcher->getNumRequests(); + static LLCachedControl<F32> fetch_pressure_scale(gSavedSettings, "TextureFetchPressureScale", 1000.f); + F32 scale = llmax((F32)fetch_pressure_scale, 1.f); + F32 fetch_pressure = llclamp((F32)pending / scale, 0.f, 3.f); + sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.f + fetch_pressure); + } + sDesiredDiscardBias = llclamp(sDesiredDiscardBias, 1.f, 4.f); if (last_texture_update_count_bias < sDesiredDiscardBias) { @@ -638,8 +701,6 @@ void LLViewerTexture::updateClass() // a problem. last_texture_update_count_bias = sDesiredDiscardBias; } - - LLViewerTexture::sFreezeImageUpdates = false; } //static @@ -1156,8 +1217,10 @@ void LLViewerFetchedTexture::init(bool firstinit) mRequestedDownloadPriority = 0.f; mFullyLoaded = false; mCanUseHTTP = true; - mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; - mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; + mDesiredDiscardLevel = S8_MAX; + // S8_MAX = no cap. setMinDiscardLevel takes min(current, new), so + // explicit caps from terrain / avatar self / thumbnails still apply. + mMinDesiredDiscardLevel = S8_MAX; mDecodingAux = false; @@ -1977,21 +2040,10 @@ bool LLViewerFetchedTexture::processFetchResults(S32& desired_discard, S32 curre setIsMissingAsset(); desired_discard = -1; } - else - { - //LL_WARNS() << mID << ": Setting min discard to " << current_discard << LL_ENDL; - if (current_discard >= 0) - { - mMinDiscardLevel = current_discard; - //desired_discard = current_discard; - } - else - { - S32 dis_level = getDiscardLevel(); - mMinDiscardLevel = dis_level; - //desired_discard = dis_level; - } - } + // Transient failure (decoder OOM, network blip): don't latch + // mMinDiscardLevel - that would block all future fetches via + // the make_request gate. Permanent failures are caught above + // (getDiscardLevel()<0 -> setIsMissingAsset). destroyRawImage(); } else if (mRawImage.notNull()) @@ -2072,8 +2124,18 @@ bool LLViewerFetchedTexture::updateFetch() if (mRawImage.notNull()) sRawCount--; if (mAuxRawImage.notNull()) sAuxCount--; // keep in mind that fetcher still might need raw image, don't modify original + S32 codec_levels = 0; bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mFetchState, mRawImage, mAuxRawImage, - mLastHttpGetStatus); + mLastHttpGetStatus, codec_levels); + if (codec_levels > 0) + { + mCodecMaxDiscardLevel = (S8)llmin(codec_levels, (S32)S8_MAX); + if (codec_levels > 5) + { + LL_DEBUGS("TextureStream") << "Texture " << mID << " codec-reported max discard " + << codec_levels << " (above the historical hardcoded cap of 5)" << LL_ENDL; + } + } if (mRawImage.notNull()) sRawCount++; if (mAuxRawImage.notNull()) { @@ -2108,7 +2170,9 @@ bool LLViewerFetchedTexture::updateFetch() } } - desired_discard = llmin(desired_discard, getMaxDiscardLevel()); + // Clamp the fetch request to what the codestream encodes; deeper + // discards are served from the GL mip pyramid via scaleDown. + desired_discard = llmin(desired_discard, (S32)mCodecMaxDiscardLevel); bool make_request = true; if (decode_priority <= 0) @@ -2116,9 +2180,14 @@ bool LLViewerFetchedTexture::updateFetch() LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - priority <= 0"); make_request = false; } - else if (mDesiredDiscardLevel > getMaxDiscardLevel()) + else if (mDesiredDiscardLevel > (S32)mCodecMaxDiscardLevel && + current_discard >= 0 && + current_discard <= (S32)mCodecMaxDiscardLevel) { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > max"); + // scaleDown can serve this from the GL pyramid. (If current is + // already past codec_max, fall through so a zoom-in can rebuild — + // scaleDown only goes deeper.) + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - desired > codec max"); make_request = false; } else if (mNeedsCreateTexture || mIsMissingAsset) @@ -2570,20 +2639,21 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() S32 gl_discard = getDiscardLevel(); - // If we don't have a legit GL image, set it to be lower than the worst discard level + // S32_MAX is the "no data" sentinel; real discards can now exceed + // MAX_DISCARD_LEVEL via dimDerivedMaxDiscard. if (gl_discard == -1) { - gl_discard = MAX_DISCARD_LEVEL + 1; + gl_discard = S32_MAX; } // // Determine the quality levels of textures that we can provide to callbacks // and whether we need to do decompression/readback to get it // - S32 current_raw_discard = MAX_DISCARD_LEVEL + 1; // We can always do a readback to get a raw discard + S32 current_raw_discard = S32_MAX; // We can always do a readback to get a raw discard S32 best_raw_discard = gl_discard; // Current GL quality level - S32 current_aux_discard = MAX_DISCARD_LEVEL + 1; - S32 best_aux_discard = MAX_DISCARD_LEVEL + 1; + S32 current_aux_discard = S32_MAX; + S32 best_aux_discard = S32_MAX; LLImageRaw *current_raw_image = nullptr; if (mIsRawImageValid) @@ -2683,7 +2753,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() // // Run raw/auxiliary data callbacks // - if (run_raw_callbacks && current_raw_image != nullptr && (current_raw_discard <= getMaxDiscardLevel())) + if (run_raw_callbacks && current_raw_image != nullptr && current_raw_discard != S32_MAX) { // Do callbacks which require raw image data. //LL_INFOS() << "doLoadedCallbacks raw for " << getID() << LL_ENDL; @@ -2723,7 +2793,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() // // Run GL callbacks // - if (run_gl_callbacks && (gl_discard <= getMaxDiscardLevel())) + if (run_gl_callbacks && gl_discard != S32_MAX) { //LL_INFOS() << "doLoadedCallbacks GL for " << getID() << LL_ENDL; @@ -3026,11 +3096,6 @@ S8 LLViewerLODTexture::getType() const return LLViewerTexture::LOD_TEXTURE; } -bool LLViewerLODTexture::isUpdateFrozen() -{ - return LLViewerTexture::sFreezeImageUpdates; -} - // This is gauranteed to get called periodically for every texture //virtual void LLViewerLODTexture::processTextureStats() @@ -3056,18 +3121,15 @@ void LLViewerLODTexture::processTextureStats() { mDesiredDiscardLevel = 0; } - // Generate the request priority and render priority - else if (mDontDiscard || !mUseMipMaps) + // HUD/UI/preview and mDontDiscard textures bypass streaming - no + // face_distance signal applies, they need native resolution. + else if (mBoostLevel >= LLGLTexture::BOOST_HIGH + || mDontDiscard + || !mUseMipMaps) { mDesiredDiscardLevel = 0; if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT) - mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 2048 and max size ever is 4096 - } - else if (mBoostLevel < LLGLTexture::BOOST_HIGH && mMaxVirtualSize <= 10.f) - { - // If the image has not been significantly visible in a while, we don't want it - mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1)); - mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); + mDesiredDiscardLevel = 1; // 4096^2 source can't be loaded full res } else if (!mFullWidth || !mFullHeight) { @@ -3076,28 +3138,86 @@ void LLViewerLODTexture::processTextureStats() } else { - //static const F64 log_2 = log(2.0); - static const F64 log_4 = log(4.0); - F32 discard_level = 0.f; - // If we know the output width and height, we can force the discard - // level to the correct value, and thus not decode more texture - // data than we need to. + // floor(log2(max(w, h))) - both the multiplier on the normalized + // factor and the cap clamp at the bottom of this function. + S32 dim_max_for_image_i = (mFullWidth > 0 && mFullHeight > 0) + ? LLImageGL::dimDerivedMaxDiscard(mFullWidth, mFullHeight) + : (S32)mCodecMaxDiscardLevel; + F32 dim_max_for_image = (F32)dim_max_for_image_i; + if (mKnownDrawWidth && mKnownDrawHeight) { + // UI-pinned target dimensions - use pixel-area math. + static const F64 log_4 = log(4.0); S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight; draw_texels = llclamp(draw_texels, MIN_IMAGE_AREA, MAX_IMAGE_AREA); - - // Use log_4 because we're in square-pixel space, so an image - // with twice the width and twice the height will have mTexelsPerImage - // 4 * draw_size discard_level = (F32)(log(mTexelsPerImage / draw_texels) / log_4); } else { - // Calculate the required scale factor of the image using pixels per texel - discard_level = (F32)(log(mTexelsPerImage / mMaxVirtualSize) / log_4); + // Two 0..1 signals composed multiplicatively: + // discard = distance_factor * size_factor * max_discard + // distance_factor: face_dist / draw_dist, shaped by + // TextureDistanceDiscardPower (default 0.5 = sqrt). + // size_factor: 1 - (mMaxOnScreenSize / window_pixels), shaped + // by TextureSizeDiscardPower. + // Either factor near 0 keeps the result fine - both have to + // be high for the texture to go deep. + static LLCachedControl<F32> distance_power(gSavedSettings, "TextureDistanceDiscardPower", 0.5f); + F32 power = llmax((F32)distance_power, 0.0001f); + F32 distance_factor = (power == 1.f) ? mMinDistanceFactor : powf(mMinDistanceFactor, power); + + static LLCachedControl<F32> size_power(gSavedSettings, "TextureSizeDiscardPower", 1.f); + F32 sz_power = llmax((F32)size_power, 0.0001f); + F32 coverage = llclampf(mMaxOnScreenSize / sWindowPixelArea); + F32 inv_cov = 1.f - coverage; + F32 size_factor = (sz_power == 1.f) ? inv_cov : powf(inv_cov, sz_power); + + F32 combined = distance_factor * size_factor; + + // Per-channel exponent. 1.0 = baseline; <1.0 pushes combined + // toward 1 (max attenuation) faster. Edges are preserved: + // pow(0, p) = 0, pow(1, p) = 1. + S32 priority_channel = (mPriorityChannel >= 0 && mPriorityChannel < 4) ? (S32)mPriorityChannel : 1; + static LLCachedControl<LLVector4> channel_priority(gSavedSettings, "TextureChannelPriority", + LLVector4(1.f, 1.f, 1.f, 1.f)); + F32 channel_power = llmax(channel_priority().mV[priority_channel], 0.0001f); + if (channel_power != 1.f) + { + combined = powf(combined, channel_power); + } + + // Own-avatar boost: shave combined for rigged/animated faces + // on gAgentAvatarp. Preference, not exemption - applied + // before the staleness/background/pressure floors so heavy + // pressure can still evict. + if (mOnAgentAvatar) + { + static LLCachedControl<F32> agent_avatar_boost(gSavedSettings, "TextureAgentAvatarBoost", 0.5f); + combined *= llclampf((F32)agent_avatar_boost); + } + + // Staleness / background floors. Avatar bakes exempt from + // background to avoid the universal-cloud bug when re-foregrounding. + combined = llmax(combined, mStalenessFactor); + if (!isAgentAvatarBoost(mBoostLevel)) + { + combined = llmax(combined, sBackgroundFactor); + } + + // VRAM pressure: pow(combined, 1 - factor) bends the curve + // without flattening it - pow(0, p) = 0 so close textures + // (combined ~ 0) stay near 0 while mid/far push toward 1. + // Avatar bakes exempt. + if (!isAgentAvatarBoost(mBoostLevel) && sMemoryPressureFactor > 0.f) + { + F32 pressure_exp = llmax(1.f - sMemoryPressureFactor, 0.0001f); + combined = powf(combined, pressure_exp); + } + + discard_level = combined * dim_max_for_image; } discard_level = floorf(discard_level); @@ -3106,12 +3226,29 @@ void LLViewerLODTexture::processTextureStats() if (mFullWidth > max_tex_res || mFullHeight > max_tex_res) min_discard = 1.f; - discard_level = llclamp(discard_level, min_discard, (F32)MAX_DISCARD_LEVEL); + // dim_max_for_image_i is the per-texture cap. TextureMaxDiscardOverride + // raises it (debug). Codec_max applies only to fetches, not here. + static LLCachedControl<S32> max_discard_override(gSavedSettings, "TextureMaxDiscardOverride", 0); + S32 effective_cap = (max_discard_override > 0) ? (S32)max_discard_override : dim_max_for_image_i; + discard_level = llclamp(discard_level, min_discard, (F32)effective_cap); + + mDesiredDiscardLevel = llmin(effective_cap, (S32)discard_level); + + // Apply the setMinDiscardLevel cap, relaxed proportionally under + // VRAM pressure - at factor=1 the cap reaches dim_max so capped + // textures (terrain, etc.) participate fully in eviction. Caps of + // 0 (thumbnails) and avatar bakes are preserved. + S32 effective_min_cap = mMinDesiredDiscardLevel; + if (sMemoryPressureFactor > 0.f && + mMinDesiredDiscardLevel > 0 && mMinDesiredDiscardLevel < S8_MAX && + !isAgentAvatarBoost(mBoostLevel)) + { + F32 room = (F32)dim_max_for_image_i - (F32)mMinDesiredDiscardLevel; + effective_min_cap += (S32)(sMemoryPressureFactor * room); + effective_min_cap = llmin(effective_min_cap, dim_max_for_image_i); + } + mDesiredDiscardLevel = llmin((S8)effective_min_cap, mDesiredDiscardLevel); - // Can't go higher than the max discard level - mDesiredDiscardLevel = llmin(getMaxDiscardLevel() + 1, (S32)discard_level); - // Clamp to min desired discard - mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, mDesiredDiscardLevel); // // At this point we've calculated the quality level that we want, @@ -3120,7 +3257,9 @@ void LLViewerLODTexture::processTextureStats() // S32 current_discard = getDiscardLevel(); - if (mBoostLevel < LLGLTexture::BOOST_AVATAR_BAKED) + // Avatar bakes exempt: shrinking mid-bake can leave the avatar + // stuck as a cloud until the next bake completes. + if (!isAgentAvatarBoost(mBoostLevel)) { if (current_discard < mDesiredDiscardLevel && !mForceToSaveRawImage) { // should scale down @@ -3128,13 +3267,6 @@ void LLViewerLODTexture::processTextureStats() } } - if (isUpdateFrozen() // we are out of memory and nearing max allowed bias - && mBoostLevel < LLGLTexture::BOOST_SCULPTED - && mDesiredDiscardLevel < current_discard) - { - // stop requesting more - mDesiredDiscardLevel = current_discard; - } mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } |
