From f91715cae18ab440d069b1ba4728f496863cbd25 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 29 Nov 2024 15:52:11 +0200 Subject: [PATCH] #3093 #3055 World Map tiles are blurry #2 --- indra/newview/lltexturefetch.cpp | 46 ++++- indra/newview/lltexturefetch.h | 18 +- indra/newview/llviewertexture.cpp | 289 +++++++++++++++++------------- indra/newview/llviewertexture.h | 2 + 4 files changed, 228 insertions(+), 127 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 58b6b5726c..556f5c89f5 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2481,7 +2481,7 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L LL_PROFILE_ZONE_SCOPED; if (mDebugPause) { - return -1; + return CREATE_REQUEST_ERROR_DEFAULT; } if (f_type == FTT_SERVER_BAKE) @@ -2497,7 +2497,7 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L << host << " != " << worker->mHost << LL_ENDL; removeRequest(worker, true); worker = NULL; - return -1; + return CREATE_REQUEST_ERROR_MHOSTS; } } @@ -2550,13 +2550,13 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L { if (worker->wasAborted()) { - return -1; // need to wait for previous aborted request to complete + return CREATE_REQUEST_ERROR_ABORTED; // need to wait for previous aborted request to complete } worker->lockWorkMutex(); // +Mw if (worker->mState == LLTextureFetchWorker::DONE && worker->mDesiredSize == llmax(desired_size, TEXTURE_CACHE_ENTRY_SIZE) && worker->mDesiredDiscard == desired_discard) { worker->unlockWorkMutex(); // -Mw - return -1; // similar request has failed or is in a transitional state + return CREATE_REQUEST_ERROR_TRANSITION; // similar request has finished, failed or is in a transitional state } worker->mActiveCount++; worker->mNeedsAux = needs_aux; @@ -3149,6 +3149,44 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r return state; } +// Threads: T* +S32 LLTextureFetch::getLastFetchState(const LLUUID& id, S32& requested_discard, S32& decoded_discard, bool& decoded) +{ + LL_PROFILE_ZONE_SCOPED; + S32 state = LLTextureFetchWorker::INVALID; + + LLTextureFetchWorker* worker = getWorker(id); + if (worker) // Don't check haveWork, intent is to get whatever is in the worker + { + worker->lockWorkMutex(); // +Mw + state = worker->mState; + requested_discard = worker->mDesiredDiscard; + decoded_discard = worker->mDecodedDiscard; + decoded = worker->mDecoded; + worker->unlockWorkMutex(); // -Mw + } + return state; +} + +// Threads: T* +S32 LLTextureFetch::getLastRawImage(const LLUUID& id, + LLPointer& raw, LLPointer& aux) +{ + LL_PROFILE_ZONE_SCOPED; + bool res = false; + S32 decoded_discard = -1; + LLTextureFetchWorker* worker = getWorker(id); + if (worker && !worker->haveWork() && worker->mDecodedDiscard >= 0) + { + worker->lockWorkMutex(); // +Mw + raw = worker->mRawImage; + aux = worker->mAuxImage; + decoded_discard = worker->mDecodedDiscard; + worker->unlockWorkMutex(); // -Mw + } + return decoded_discard; +} + void LLTextureFetch::dump() { LL_INFOS(LOG_TXT) << "LLTextureFetch ACTIVE_HTTP:" << LL_ENDL; diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index c2c5ec5acc..8ab90896dc 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -76,6 +76,14 @@ class LLTextureFetch : public LLWorkerThread // Threads: Tmain void shutDownImageDecodeThread(); + enum e_crete_request_errors + { + CREATE_REQUEST_ERROR_DEFAULT = -1, + CREATE_REQUEST_ERROR_MHOSTS = -2, + CREATE_REQUEST_ERROR_ABORTED = -3, + CREATE_REQUEST_ERROR_TRANSITION = -4, + }; + // Threads: T* (but Tmain mostly) S32 createRequest(FTType f_type, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); @@ -114,12 +122,20 @@ class LLTextureFetch : public LLWorkerThread // get the current fetch state, if any, from the given UUID S32 getFetchState(const LLUUID& id); - // @return Fetch state of given image and associates statistics + // @return Fetch state of an active given image and associates statistics // See also getStateString // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + // @return Fetch last state of given image + // Threads: T* + S32 getLastFetchState(const LLUUID& id, S32& requested_discard, S32 &decoded_discard, bool &decoded); + + // @return Fetch last raw image + // Threads: T* + S32 getLastRawImage(const LLUUID& id, LLPointer& raw, LLPointer& aux); + // Debug utility - generally not safe void dump(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 743b930e90..aa414a0f63 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1805,6 +1805,137 @@ 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"); + setDimensions(mRawImage->getWidth() << mRawDiscardLevel, + mRawImage->getHeight() << mRawDiscardLevel); + + if (getFullWidth() > MAX_IMAGE_SIZE || getFullHeight() > 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; @@ -1889,126 +2020,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"); - setDimensions(mRawImage->getWidth() << mRawDiscardLevel, - mRawImage->getHeight() << mRawDiscardLevel); - - if(getFullWidth() > MAX_IMAGE_SIZE || getFullHeight() > 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) - && 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(); - } - } - 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) @@ -2083,21 +2100,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 @@ -2919,7 +2964,7 @@ void LLViewerLODTexture::processTextureStats() mDesiredDiscardLevel = 0; } // Generate the request priority and render priority - else if (mDontDiscard || !mUseMipMaps || (getFTType() == FTT_MAP_TILE)) + else if (mDontDiscard || !mUseMipMaps) { mDesiredDiscardLevel = 0; if (getFullWidth() > MAX_IMAGE_SIZE_DEFAULT || getFullHeight() > MAX_IMAGE_SIZE_DEFAULT) diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index a4775d25b7..bff778c52d 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -421,6 +421,8 @@ class LLViewerFetchedTexture : public LLViewerTexture void init(bool firstinit) ; void cleanup() ; + bool processFetchResults(S32& desired_discard, S32 current_discard, S32 fetch_discard, F32 decode_priority); + void saveRawImage() ; private: