diff options
Diffstat (limited to 'indra/newview/llviewertexture.cpp')
-rw-r--r-- | indra/newview/llviewertexture.cpp | 404 |
1 files changed, 226 insertions, 178 deletions
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 99f8db00f2..36b6787ace 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -43,6 +43,7 @@ #include "message.h" #include "lltimer.h" #include "v4coloru.h" +#include "llnotificationsutil.h" // viewer includes #include "llimagegl.h" @@ -70,6 +71,7 @@ LLPointer<LLViewerTexture> LLViewerTexture::sBlackImagep = nullptr; LLPointer<LLViewerTexture> LLViewerTexture::sCheckerBoardImagep = nullptr; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = nullptr; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = nullptr; +LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultParticleImagep = nullptr; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = nullptr; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sSmokeImagep = nullptr; LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sFlatNormalImagep = nullptr; @@ -497,16 +499,19 @@ void LLViewerTexture::updateClass() F64 texture_bytes_alloc = LLImageGL::getTextureBytesAllocated() / 1024.0 / 512.0; F64 vertex_bytes_alloc = LLVertexBuffer::getBytesAllocated() / 1024.0 / 512.0; - F64 render_bytes_alloc = LLRenderTarget::sBytesAllocated / 1024.0 / 512.0; // 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 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc + render_bytes_alloc); + F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc); F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget; - // try to leave half a GB for everyone else, but keep at least 768MB for ourselves - F32 target = llmax(budget - 512.f, MIN_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 target = llmax(llmin(budget - 512.f, budget * 0.8f), MIN_VRAM_BUDGET); sFreeVRAMMegabytes = target - used; F32 over_pct = (used - target) / target; @@ -522,7 +527,7 @@ void LLViewerTexture::updateClass() // 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) + 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) @@ -541,8 +546,9 @@ void LLViewerTexture::updateClass() if (sEvaluationTimer.getElapsedTimeF32() > MEMORY_CHECK_WAIT_TIME) { static LLCachedControl<F32> low_mem_min_discard_increment(gSavedSettings, "RenderLowMemMinDiscardIncrement", .1f); - sDesiredDiscardBias += (F32)low_mem_min_discard_increment * (F32)gFrameIntervalSeconds; - sEvaluationTimer.reset(); + + F32 increment = low_mem_min_discard_increment + llmax(over_pct, 0.f); + sDesiredDiscardBias += increment * gFrameIntervalSeconds; } } else @@ -553,26 +559,36 @@ void LLViewerTexture::updateClass() // lower discard bias over time when free memory is available if (sDesiredDiscardBias > 1.f && over_pct < 0.f) { - sDesiredDiscardBias -= gFrameIntervalSeconds * 0.01f; + static LLCachedControl<F32> high_mem_discard_decrement(gSavedSettings, "RenderHighMemMinDiscardDecrement", .1f); + + F32 decrement = high_mem_discard_decrement - llmin(over_pct, 0.f); + sDesiredDiscardBias -= decrement * gFrameIntervalSeconds; } } // set to max discard bias if the window has been backgrounded for a while + static F32 last_desired_discard_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); bool in_background = (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) || !gFocusMgr.getAppHasFocus(); - + bool is_minimized = gViewerWindow && gViewerWindow->getWindow()->getMinimized() && in_background; if (in_background) { - if (backgrounded_timer.getElapsedTimeF32() > 10.f) + F32 discard_time = is_minimized ? minimized_discard_time : backgrounded_discard_time; + if (discard_time > 0.f && backgrounded_timer.getElapsedTimeF32() > discard_time) { if (!was_backgrounded) { - LL_INFOS() << "Viewer is backgrounded, freeing up video memory." << LL_ENDL; + 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; } - was_backgrounded = true; - sDesiredDiscardBias = 4.f; + sDesiredDiscardBias = 5.f; } } else @@ -580,9 +596,9 @@ void LLViewerTexture::updateClass() backgrounded_timer.reset(); if (was_backgrounded) { // if the viewer was backgrounded - LL_INFOS() << "Viewer is no longer backgrounded, resuming normal texture usage." << LL_ENDL; + LL_INFOS() << "Viewer is no longer backgrounded or minimized, resuming normal texture usage." << LL_ENDL; was_backgrounded = false; - sDesiredDiscardBias = 1.f; + sDesiredDiscardBias = last_desired_discard_bias; } } @@ -1361,51 +1377,6 @@ void LLViewerFetchedTexture::addToCreateTexture() } else { - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; -#if 1 - // - //if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up, - //so do not scale down the over qualified image. - //Note: scaling down image is expensensive. Do it only when very necessary. - // - if(mRequestedDiscardLevel <= mDesiredDiscardLevel && !mForceToSaveRawImage) - { - U32 w = mFullWidth >> mRawDiscardLevel; - U32 h = mFullHeight >> mRawDiscardLevel; - - //if big image, do not load extra data - //scale it down to size >= LLViewerTexture::sMinLargeImageSize - if(w * h > LLViewerTexture::sMinLargeImageSize) - { - S32 d_level = llmin(mRequestedDiscardLevel, (S32)mDesiredDiscardLevel) - mRawDiscardLevel; - - if(d_level > 0) - { - S32 i = 0; - while((d_level > 0) && ((w >> i) * (h >> i) > LLViewerTexture::sMinLargeImageSize)) - { - i++; - d_level--; - } - if(i > 0) - { - mRawDiscardLevel += i; - if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0) - { - mNeedsCreateTexture = false; - destroyRawImage(); - return; - } - - { - //make a duplicate in case somebody else is using this raw image - mRawImage = mRawImage->scaled(w >> i, h >> i); - } - } - } - } - } -#endif scheduleCreateTexture(); } return; @@ -1567,8 +1538,8 @@ void LLViewerFetchedTexture::postCreateTexture() if (!needsToSaveRawImage()) { mNeedsAux = false; - destroyRawImage(); } + destroyRawImage(); // will save raw image if needed mNeedsCreateTexture = false; } @@ -1703,6 +1674,7 @@ void LLViewerFetchedTexture::processTextureStats() if(mDesiredDiscardLevel > mMinDesiredDiscardLevel)//need to load more { mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); mFullyLoaded = false; } //setDebugText("fully loaded"); @@ -1752,6 +1724,7 @@ void LLViewerFetchedTexture::processTextureStats() log((F32)mFullHeight / mKnownDrawHeight) / log_2); mDesiredDiscardLevel = llclamp(mDesiredDiscardLevel, (S8)0, (S8)getMaxDiscardLevel()); mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } mKnownDrawSizeChanged = false; @@ -1816,6 +1789,141 @@ void LLViewerFetchedTexture::setBoostLevel(S32 level) } } +bool LLViewerFetchedTexture::processFetchResults(S32& desired_discard, S32 current_discard, S32 fetch_discard, F32 decode_priority) +{ + // We may have data ready regardless of whether or not we are finished (e.g. waiting on write) + if (mRawImage.notNull()) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - has raw image"); + LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + mIsFetched = true; + tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)); + } + mRawDiscardLevel = fetch_discard; + if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && + (current_discard < 0 || mRawDiscardLevel < current_discard)) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - data good"); + + // This is going to conflict with Develop, just pick from develop + // where it uses setDimensions instead of setTexelsPerImage + mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; + mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + setTexelsPerImage(); + + if (mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) + { + //discard all oversized textures. + destroyRawImage(); + LL_WARNS() << "oversize, setting as missing" << LL_ENDL; + setIsMissingAsset(); + mRawDiscardLevel = INVALID_DISCARD_LEVEL; + mIsFetching = false; + mLastPacketTimer.reset(); + } + else + { + mIsRawImageValid = true; + addToCreateTexture(); + } + + if (mBoostLevel == LLGLTexture::BOOST_ICON) + { + S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS; + S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS; + if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) + { + // scale oversized icon, no need to give more work to gl + // since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy + // + // BOOST_ICON gets scaling because profile icons can have a bunch of different formats, not just j2c + // Might need another pass to use discard for j2c and scaling for everything else. + mRawImage = mRawImage->scaled(expected_width, expected_height); + } + } + + if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL) + { + S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS; + S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS; + if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) + { + // scale oversized icon, no need to give more work to gl + // since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy + // + // Todo: probably needs to be remade to use discard, all thumbnails are supposed to be j2c, + // so no need to scale, should be posible to use discard to scale image down. + mRawImage = mRawImage->scaled(expected_width, expected_height); + } + } + + return true; + } + else + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - data not needed"); + // Data is ready but we don't need it + // (received it already while fetcher was writing to disk) + destroyRawImage(); + return false; // done + } + } + + if (!mIsFetching) + { + if ((decode_priority > 0) + && (mRawDiscardLevel < 0 || mRawDiscardLevel == INVALID_DISCARD_LEVEL) + && mFetchState > 1) // 1 - initial, make sure fetcher did at least something + { + // We finished but received no data + if (getDiscardLevel() < 0) + { + if (getFTType() != FTT_MAP_TILE) + { + LL_WARNS() << mID + << " Fetch failure, setting as missing, decode_priority " << decode_priority + << " mRawDiscardLevel " << mRawDiscardLevel + << " current_discard " << current_discard + << " stats " << mLastHttpGetStatus.toHex() + << " worker state " << mFetchState + << LL_ENDL; + } + 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; + } + } + destroyRawImage(); + } + else if (mRawImage.notNull()) + { + // We have data, but our fetch failed to return raw data + // *TODO: FIgure out why this is happening and fix it + // Potentially can happen when TEX_LIST_SCALE and TEX_LIST_STANDARD + // get requested for the same texture id at the same time + // (two textures, one fetcher) + destroyRawImage(); + } + } + + return true; +} + bool LLViewerFetchedTexture::updateFetch() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -1880,7 +1988,7 @@ 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 - bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage, + bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mFetchState, mRawImage, mAuxRawImage, mLastHttpGetStatus); if (mRawImage.notNull()) sRawCount++; if (mAuxRawImage.notNull()) @@ -1900,121 +2008,12 @@ bool LLViewerFetchedTexture::updateFetch() mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); } - // We may have data ready regardless of whether or not we are finished (e.g. waiting on write) - if (mRawImage.notNull()) + if (!processFetchResults(desired_discard, current_discard, fetch_discard, decode_priority)) { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - has raw image"); - LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); - if (tester) - { - mIsFetched = true; - tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)); - } - mRawDiscardLevel = fetch_discard; - if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && - (current_discard < 0 || mRawDiscardLevel < current_discard)) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - data good"); - mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; - mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; - setTexelsPerImage(); - - if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE) - { - //discard all oversized textures. - destroyRawImage(); - LL_WARNS() << "oversize, setting as missing" << LL_ENDL; - setIsMissingAsset(); - mRawDiscardLevel = INVALID_DISCARD_LEVEL; - mIsFetching = false; - mLastPacketTimer.reset(); - } - else - { - mIsRawImageValid = true; - addToCreateTexture(); - } - - if (mBoostLevel == LLGLTexture::BOOST_ICON) - { - S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_ICON_DIMENSIONS; - S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_ICON_DIMENSIONS; - if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) - { - // scale oversized icon, no need to give more work to gl - // since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy - mRawImage = mRawImage->scaled(expected_width, expected_height); - } - } - - if (mBoostLevel == LLGLTexture::BOOST_THUMBNAIL) - { - S32 expected_width = mKnownDrawWidth > 0 ? mKnownDrawWidth : DEFAULT_THUMBNAIL_DIMENSIONS; - S32 expected_height = mKnownDrawHeight > 0 ? mKnownDrawHeight : DEFAULT_THUMBNAIL_DIMENSIONS; - if (mRawImage && (mRawImage->getWidth() > expected_width || mRawImage->getHeight() > expected_height)) - { - // scale oversized icon, no need to give more work to gl - // since we got mRawImage from thread worker and image may be in use (ex: writing cache), make a copy - mRawImage = mRawImage->scaled(expected_width, expected_height); - } - } - - return true; - } - else - { - LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - data not needed"); - // Data is ready but we don't need it - // (received it already while fetcher was writing to disk) - destroyRawImage(); - return false; // done - } + return false; } - if (!mIsFetching) - { - if ((decode_priority > 0) && (mRawDiscardLevel < 0 || mRawDiscardLevel == INVALID_DISCARD_LEVEL)) - { - // We finished but received no data - if (getDiscardLevel() < 0) - { - if (getFTType() != FTT_MAP_TILE) - { - LL_WARNS() << mID - << " Fetch failure, setting as missing, decode_priority " << decode_priority - << " mRawDiscardLevel " << mRawDiscardLevel - << " current_discard " << current_discard - << " stats " << mLastHttpGetStatus.toHex() - << LL_ENDL; - } - 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; - } - } - destroyRawImage(); - } - else if (mRawImage.notNull()) - { - // We have data, but our fetch failed to return raw data - // *TODO: FIgure out why this is happening and fix it - destroyRawImage(); - } - } - else + if (mIsFetching) { static const F32 MAX_HOLD_TIME = 5.0f; //seconds to wait before canceling fecthing if decode_priority is 0.f. if(decode_priority > 0.0f || mStopFetchingTimer.getElapsedTimeF32() > MAX_HOLD_TIME) @@ -2089,21 +2088,49 @@ bool LLViewerFetchedTexture::updateFetch() } // bypass texturefetch directly by pulling from LLTextureCache - S32 fetch_request_discard = -1; - fetch_request_discard = LLAppViewer::getTextureFetch()->createRequest(mFTType, mUrl, getID(), getTargetHost(), decode_priority, + S32 fetch_request_response = -1; + S32 worker_discard = -1; + fetch_request_response = LLAppViewer::getTextureFetch()->createRequest(mFTType, mUrl, getID(), getTargetHost(), decode_priority, w, h, c, desired_discard, needsAux(), mCanUseHTTP); - if (fetch_request_discard >= 0) + if (fetch_request_response >= 0) // positive values and 0 are discard values { LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - request created"); mHasFetcher = true; mIsFetching = true; // in some cases createRequest can modify discard, as an example // bake textures are always at discard 0 - mRequestedDiscardLevel = llmin(desired_discard, fetch_request_discard); + mRequestedDiscardLevel = llmin(desired_discard, fetch_request_response); mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP); } + else if (fetch_request_response == LLTextureFetch::CREATE_REQUEST_ERROR_TRANSITION) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_TEXTURE("vftuf - processing transition error"); + // Request wasn't created because similar one finished or is in a transitional state, check worker state + // As an example can happen if an image (like a server bake always fetches at dis 0), was scaled down to + // needed discard after fetching then sudenly needed higher dis and worker wasn't yet deleted. Worker + // discard will be identical to requested one and worker will have nothing new to do despite GL image + // not being up to data. + S32 desired_discard; + S32 decoded_discard; + bool decoded; + S32 fetch_state = LLAppViewer::getTextureFetch()->getLastFetchState(mID, desired_discard, decoded_discard, decoded); + if (fetch_state > 1 && decoded && decoded_discard >=0 && decoded_discard <= desired_discard) + { + // worker actually has the image + if (mRawImage.notNull()) sRawCount--; + if (mAuxRawImage.notNull()) sAuxCount--; + decoded_discard = LLAppViewer::getTextureFetch()->getLastRawImage(getID(), mRawImage, mAuxRawImage); + if (mRawImage.notNull()) sRawCount++; + if (mAuxRawImage.notNull()) + { + mHasAux = true; + sAuxCount++; + } + processFetchResults(desired_discard, current_discard, decoded_discard, decode_priority); + } + } // If createRequest() failed, that means one of two things: // 1. We're finishing up a request for this UUID, so we @@ -2476,6 +2503,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() if (mIsRawImageValid) { // If we have an existing raw image, we have a baseline for the raw and auxiliary quality levels. + current_raw_discard = mRawDiscardLevel; best_raw_discard = llmin(best_raw_discard, mRawDiscardLevel); best_aux_discard = llmin(best_aux_discard, mRawDiscardLevel); // We always decode the aux when we decode the base raw current_aux_discard = llmin(current_aux_discard, best_aux_discard); @@ -2540,6 +2568,11 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() } } + if (need_readback) + { + readbackRawImage(); + } + // // Run raw/auxiliary data callbacks // @@ -2789,10 +2822,22 @@ void LLViewerFetchedTexture::readbackRawImage() if (mGLTexturep.notNull() && mGLTexturep->getTexName() != 0 && (mRawImage.isNull() || mRawImage->getWidth() < mGLTexturep->getWidth() || mRawImage->getHeight() < mGLTexturep->getHeight() )) { + if (mRawImage.isNull()) + { + sRawCount++; + } mRawImage = new LLImageRaw(); if (!mGLTexturep->readBackRaw(-1, mRawImage, false)) { mRawImage = nullptr; + mIsRawImageValid = false; + mRawDiscardLevel = INVALID_DISCARD_LEVEL; + sRawCount--; + } + else + { + mIsRawImageValid = true; + mRawDiscardLevel = mGLTexturep->getDiscardLevel(); } } } @@ -2917,10 +2962,12 @@ void LLViewerLODTexture::processTextureStats() { // 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) { mDesiredDiscardLevel = getMaxDiscardLevel(); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } else { @@ -2990,6 +3037,7 @@ void LLViewerLODTexture::processTextureStats() // stop requesting more mDesiredDiscardLevel = current_discard; } + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0) |