diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-06-20 12:28:09 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-06-20 12:28:09 -0400 |
commit | d110358472b83f2f31d60ea0d76f1b426a087f56 (patch) | |
tree | 83617196e7d444c1063075e4a4c50fe19490a4ce /indra/newview/lltexturefetch.cpp | |
parent | bb1f3f08cf93facbf926e57384674441be7e2884 (diff) | |
parent | e92689063bdbe34907348a12f1db39bc81132783 (diff) |
Merge branch 'release/luau-scripting' into lua-speedometer-demo
Diffstat (limited to 'indra/newview/lltexturefetch.cpp')
-rw-r--r-- | indra/newview/lltexturefetch.cpp | 5050 |
1 files changed, 2525 insertions, 2525 deletions
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 40bbe2b934..77c28bd3f4 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1,25 +1,25 @@ -/** +/** * @file lltexturefetch.cpp * @brief Object which fetches textures from the cache and/or network * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012-2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -212,8 +212,8 @@ const std::string sTesterName("TextureFetchTester"); // Worker State Machine // // "doWork" will be executed for a given worker on its respective -// LLQueuedThread. If doWork returns true, the worker is treated -// as completed. If doWork returns false, the worker will be +// LLQueuedThread. If doWork returns true, the worker is treated +// as completed. If doWork returns false, the worker will be // put on the back of the work queue at the start of the next iteration // of the mainloop. If a worker is waiting on a resource, it should // return false as soon as possible and not block to avoid starving @@ -226,8 +226,8 @@ const std::string sTesterName("TextureFetchTester"); // Tuning/Parameterization Constants -static const S32 HTTP_PIPE_REQUESTS_HIGH_WATER = 100; // Maximum requests to have active in HTTP (pipelined) -static const S32 HTTP_PIPE_REQUESTS_LOW_WATER = 50; // Active level at which to refill +static const S32 HTTP_PIPE_REQUESTS_HIGH_WATER = 100; // Maximum requests to have active in HTTP (pipelined) +static const S32 HTTP_PIPE_REQUESTS_LOW_WATER = 50; // Active level at which to refill static const S32 HTTP_NONPIPE_REQUESTS_HIGH_WATER = 40; static const S32 HTTP_NONPIPE_REQUESTS_LOW_WATER = 20; @@ -247,12 +247,12 @@ static const S32 CAP_MISSING_EXPIRATION_DELAY = 1; // seconds namespace { // The NoOpDeletor is used when passing certain objects (the LLTextureFetchWorker) - // in a smart pointer below for passage into - // the LLCore::Http libararies. When the smart pointer is destroyed, no - // action will be taken since we do not in these cases want the object to + // in a smart pointer below for passage into + // the LLCore::Http libararies. When the smart pointer is destroyed, no + // action will be taken since we do not in these cases want the object to // be destroyed at the end of the call. - // - // *NOTE$: Yes! It is "Deletor" + // + // *NOTE$: Yes! It is "Deletor" // http://english.stackexchange.com/questions/4733/what-s-the-rule-for-adding-er-vs-or-when-nouning-a-verb // "delete" derives from Latin "deletus" void NoOpDeletor(LLCore::HttpHandler *) @@ -261,20 +261,20 @@ namespace static const char* e_state_name[] = { - "INVALID", - "INIT", - "LOAD_FROM_TEXTURE_CACHE", - "CACHE_POST", - "LOAD_FROM_NETWORK", - "WAIT_HTTP_RESOURCE", - "WAIT_HTTP_RESOURCE2", - "SEND_HTTP_REQ", - "WAIT_HTTP_REQ", - "DECODE_IMAGE", - "DECODE_IMAGE_UPDATE", - "WRITE_TO_CACHE", - "WAIT_ON_WRITE", - "DONE" + "INVALID", + "INIT", + "LOAD_FROM_TEXTURE_CACHE", + "CACHE_POST", + "LOAD_FROM_NETWORK", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", + "WAIT_HTTP_REQ", + "DECODE_IMAGE", + "DECODE_IMAGE_UPDATE", + "WRITE_TO_CACHE", + "WAIT_ON_WRITE", + "DONE" }; // Log scope @@ -283,346 +283,346 @@ static const char * const LOG_TXT = "Texture"; class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler { - friend class LLTextureFetch; - + friend class LLTextureFetch; + private: - class CacheReadResponder : public LLTextureCache::ReadResponder - { - public: - - // Threads: Ttf - CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) - : mFetcher(fetcher), mID(id) - { - setImage(image); - } - - // Threads: Ttc - virtual void completed(bool success) - { + class CacheReadResponder : public LLTextureCache::ReadResponder + { + public: + + // Threads: Ttf + CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) + : mFetcher(fetcher), mID(id) + { + setImage(image); + } + + // Threads: Ttc + virtual void completed(bool success) + { LL_PROFILE_ZONE_SCOPED; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal); - } - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; - - class CacheWriteResponder : public LLTextureCache::WriteResponder - { - public: - - // Threads: Ttf - CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) - : mFetcher(fetcher), mID(id) - { - } - - // Threads: Ttc - virtual void completed(bool success) - { + LLTextureFetchWorker* worker = mFetcher->getWorker(mID); + if (worker) + { + worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal); + } + } + private: + LLTextureFetch* mFetcher; + LLUUID mID; + }; + + class CacheWriteResponder : public LLTextureCache::WriteResponder + { + public: + + // Threads: Ttf + CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) + : mFetcher(fetcher), mID(id) + { + } + + // Threads: Ttc + virtual void completed(bool success) + { LL_PROFILE_ZONE_SCOPED; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - worker->callbackCacheWrite(success); - } - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; - - class DecodeResponder : public LLImageDecodeThread::Responder - { - public: - - // Threads: Ttf - DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) - : mFetcher(fetcher), mID(id) - { - } - - // Threads: Tid - virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) - { + LLTextureFetchWorker* worker = mFetcher->getWorker(mID); + if (worker) + { + worker->callbackCacheWrite(success); + } + } + private: + LLTextureFetch* mFetcher; + LLUUID mID; + }; + + class DecodeResponder : public LLImageDecodeThread::Responder + { + public: + + // Threads: Ttf + DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) + : mFetcher(fetcher), mID(id) + { + } + + // Threads: Tid + virtual void completed(bool success, const std::string& error_message, LLImageRaw* raw, LLImageRaw* aux, U32 request_id) + { LL_PROFILE_ZONE_SCOPED; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - worker->callbackDecoded(success, raw, aux, request_id); - } - } - private: - LLTextureFetch* mFetcher; - LLUUID mID; - }; - - struct Compare - { - // lhs < rhs - bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const - { - // greater priority is "less" + LLTextureFetchWorker* worker = mFetcher->getWorker(mID); + if (worker) + { + worker->callbackDecoded(success, error_message, raw, aux, request_id); + } + } + private: + LLTextureFetch* mFetcher; + LLUUID mID; + }; + + struct Compare + { + // lhs < rhs + bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const + { + // greater priority is "less" return lhs->mImagePriority > rhs->mImagePriority; - } - }; - + } + }; + public: - // Threads: Ttf - /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() - - // Threads: Ttf - /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - - // Threads: Tmain - /*virtual*/ bool deleteOK(); // called from update() - - ~LLTextureFetchWorker(); - - // Threads: Ttf - // Locks: Mw - S32 callbackHttpGet(LLCore::HttpResponse * response, - bool partial, bool success); - - // Threads: Ttc - void callbackCacheRead(bool success, LLImageFormatted* image, - S32 imagesize, BOOL islocal); - - // Threads: Ttc - void callbackCacheWrite(bool success); - - // Threads: Tid - void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux, S32 decode_id); - - // Threads: T* - void setGetStatus(LLCore::HttpStatus status, const std::string& reason) - { - LLMutexLock lock(&mWorkMutex); - - mGetStatus = status; - mGetReason = reason; - } - - void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; } - bool getCanUseHTTP() const { return mCanUseHTTP; } - - void setUrl(const std::string& url) { mUrl = url; } - - LLTextureFetch & getFetcher() { return *mFetcher; } - - // Inherited from LLCore::HttpHandler - // Threads: Ttf - virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); - - enum e_state // mState - { - // *NOTE: Do not change the order/value of state variables, some code - // depends upon specific ordering/adjacency. - - // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) - INVALID = 0, - INIT, - LOAD_FROM_TEXTURE_CACHE, - CACHE_POST, - LOAD_FROM_NETWORK, - WAIT_HTTP_RESOURCE, // Waiting for HTTP resources - WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources - SEND_HTTP_REQ, // Commit to sending as HTTP - WAIT_HTTP_REQ, // Request sent, wait for completion - DECODE_IMAGE, - DECODE_IMAGE_UPDATE, - WRITE_TO_CACHE, - WAIT_ON_WRITE, - DONE - }; + // Threads: Ttf + /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf + /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() + + ~LLTextureFetchWorker(); + + // Threads: Ttf + // Locks: Mw + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); + + // Threads: Ttc + void callbackCacheRead(bool success, LLImageFormatted* image, + S32 imagesize, BOOL islocal); + + // Threads: Ttc + void callbackCacheWrite(bool success); + + // Threads: Tid + void callbackDecoded(bool success, const std::string& error_message, LLImageRaw* raw, LLImageRaw* aux, S32 decode_id); + + // Threads: T* + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) + { + LLMutexLock lock(&mWorkMutex); + + mGetStatus = status; + mGetReason = reason; + } + + void setCanUseHTTP(bool can_use_http) { mCanUseHTTP = can_use_http; } + bool getCanUseHTTP() const { return mCanUseHTTP; } + + void setUrl(const std::string& url) { mUrl = url; } + + LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + enum e_state // mState + { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) + INVALID = 0, + INIT, + LOAD_FROM_TEXTURE_CACHE, + CACHE_POST, + LOAD_FROM_NETWORK, + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_REQ, // Request sent, wait for completion + DECODE_IMAGE, + DECODE_IMAGE_UPDATE, + WRITE_TO_CACHE, + WAIT_ON_WRITE, + DONE + }; protected: - LLTextureFetchWorker(LLTextureFetch* fetcher, FTType f_type, - const std::string& url, const LLUUID& id, const LLHost& host, - F32 priority, S32 discard, S32 size); + LLTextureFetchWorker(LLTextureFetch* fetcher, FTType f_type, + const std::string& url, const LLUUID& id, const LLHost& host, + F32 priority, S32 discard, S32 size); private: - // Threads: Tmain - /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + // Threads: Tmain + /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) - // Threads: Tmain - /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Threads: Tmain + /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + + // Locks: Mw + void resetFormattedData(); - // Locks: Mw - void resetFormattedData(); - // get the relative priority of this worker (should map to max virtual size) F32 getImagePriority() const; - // Locks: Mw - void setImagePriority(F32 priority); + // Locks: Mw + void setImagePriority(F32 priority); - // Locks: Mw (ctor invokes without lock) - void setDesiredDiscard(S32 discard, S32 size); + // Locks: Mw (ctor invokes without lock) + void setDesiredDiscard(S32 discard, S32 size); // Threads: T* - // Locks: Mw - bool insertPacket(S32 index, U8* data, S32 size); - - // Locks: Mw - void clearPackets(); - - - // Locks: Mw - void removeFromCache(); - - // Threads: Ttf - bool writeToCacheComplete(); - - // Threads: Ttf - void recordTextureStart(bool is_http); - - // Threads: Ttf - void recordTextureDone(bool is_http, F64 byte_count); - - void lockWorkMutex() { mWorkMutex.lock(); } - void unlockWorkMutex() { mWorkMutex.unlock(); } - - // Threads: Ttf - // Locks: Mw - bool acquireHttpSemaphore() - { - llassert(! mHttpHasResource); - if (mFetcher->mHttpSemaphore >= mFetcher->mHttpHighWater) - { - return false; - } - mHttpHasResource = true; - mFetcher->mHttpSemaphore++; - return true; - } - - // Threads: Ttf - // Locks: Mw - void releaseHttpSemaphore() - { - llassert(mHttpHasResource); - mHttpHasResource = false; - mFetcher->mHttpSemaphore--; - llassert_always(mFetcher->mHttpSemaphore >= 0); - } - + // Locks: Mw + bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw + void clearPackets(); + + + // Locks: Mw + void removeFromCache(); + + // Threads: Ttf + bool writeToCacheComplete(); + + // Threads: Ttf + void recordTextureStart(bool is_http); + + // Threads: Ttf + void recordTextureDone(bool is_http, F64 byte_count); + + void lockWorkMutex() { mWorkMutex.lock(); } + void unlockWorkMutex() { mWorkMutex.unlock(); } + + // Threads: Ttf + // Locks: Mw + bool acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + if (mFetcher->mHttpSemaphore >= mFetcher->mHttpHighWater) + { + return false; + } + mHttpHasResource = true; + mFetcher->mHttpSemaphore++; + return true; + } + + // Threads: Ttf + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + mFetcher->mHttpSemaphore--; + llassert_always(mFetcher->mHttpSemaphore >= 0); + } + private: - enum e_request_state // mSentRequest - { - UNSENT = 0, - QUEUED = 1, - SENT_SIM = 2 - }; - enum e_write_to_cache_state //mWriteToCacheState - { - NOT_WRITE = 0, - CAN_WRITE = 1, - SHOULD_WRITE = 2 - }; - - e_state mState; - void setState(e_state new_state); + enum e_request_state // mSentRequest + { + UNSENT = 0, + QUEUED = 1, + SENT_SIM = 2 + }; + enum e_write_to_cache_state //mWriteToCacheState + { + NOT_WRITE = 0, + CAN_WRITE = 1, + SHOULD_WRITE = 2 + }; + + e_state mState; + void setState(e_state new_state); LLViewerRegion* getRegion(); - e_write_to_cache_state mWriteToCacheState; - LLTextureFetch* mFetcher; - LLPointer<LLImageFormatted> mFormattedImage; - LLPointer<LLImageRaw> mRawImage, - mAuxImage; - FTType mFTType; - LLUUID mID; - LLHost mHost; - std::string mUrl; - U8 mType; - F32 mImagePriority; // should map to max virtual size - F32 mRequestedPriority; - S32 mDesiredDiscard; - S32 mSimRequestedDiscard; - S32 mRequestedDiscard; + e_write_to_cache_state mWriteToCacheState; + LLTextureFetch* mFetcher; + LLPointer<LLImageFormatted> mFormattedImage; + LLPointer<LLImageRaw> mRawImage, + mAuxImage; + FTType mFTType; + LLUUID mID; + LLHost mHost; + std::string mUrl; + U8 mType; + F32 mImagePriority; // should map to max virtual size + F32 mRequestedPriority; + S32 mDesiredDiscard; + S32 mSimRequestedDiscard; + S32 mRequestedDiscard; S32 mLoadedDiscard; S32 mDecodedDiscard; - LLFrameTimer mRequestedDeltaTimer; - LLFrameTimer mFetchDeltaTimer; - LLTimer mCacheReadTimer; + LLFrameTimer mRequestedDeltaTimer; + LLFrameTimer mFetchDeltaTimer; + LLTimer mCacheReadTimer; LLTimer mDecodeTimer; - LLTimer mCacheWriteTimer; + LLTimer mCacheWriteTimer; LLTimer mFetchTimer; - LLTimer mStateTimer; - F32 mCacheReadTime; // time for cache read only + LLTimer mStateTimer; + F32 mCacheReadTime; // time for cache read only F32 mDecodeTime; // time for decode only - F32 mCacheWriteTime; + F32 mCacheWriteTime; F32 mFetchTime; // total time from req to finished fetch - std::map<S32, F32> mStateTimersMap; - F32 mSkippedStatesTime; - LLTextureCache::handle_t mCacheReadHandle, - mCacheWriteHandle; - S32 mRequestedSize, - mRequestedOffset, - mDesiredSize, - mFileSize, - mCachedSize; - e_request_state mSentRequest; - handle_t mDecodeHandle; - BOOL mLoaded; - BOOL mDecoded; - BOOL mWritten; - BOOL mNeedsAux; - BOOL mHaveAllData; - BOOL mInLocalCache; - BOOL mInCache; + std::map<S32, F32> mStateTimersMap; + F32 mSkippedStatesTime; + LLTextureCache::handle_t mCacheReadHandle, + mCacheWriteHandle; + S32 mRequestedSize, + mRequestedOffset, + mDesiredSize, + mFileSize, + mCachedSize; + e_request_state mSentRequest; + handle_t mDecodeHandle; + BOOL mLoaded; + BOOL mDecoded; + BOOL mWritten; + BOOL mNeedsAux; + BOOL mHaveAllData; + BOOL mInLocalCache; + BOOL mInCache; bool mCanUseHTTP; - S32 mRetryAttempt; - S32 mActiveCount; - LLCore::HttpStatus mGetStatus; - std::string mGetReason; - LLAdaptiveRetryPolicy mFetchRetryPolicy; + S32 mRetryAttempt; + S32 mActiveCount; + LLCore::HttpStatus mGetStatus; + std::string mGetReason; + LLAdaptiveRetryPolicy mFetchRetryPolicy; bool mCanUseCapability; LLTimer mRegionRetryTimer; S32 mRegionRetryAttempt; LLUUID mLastRegionId; - - // Work Data - LLMutex mWorkMutex; - struct PacketData - { - PacketData(U8* data, S32 size) - : mData(data), mSize(size) - {} - ~PacketData() { clearData(); } - void clearData() { delete[] mData; mData = NULL; } - - U8* mData; - U32 mSize; - }; - std::vector<PacketData*> mPackets; - S32 mFirstPacket; - S32 mLastPacket; - U16 mTotalPackets; - U8 mImageCodec; - - LLViewerAssetStats::duration_t mMetricsStartTime; - - LLCore::HttpHandle mHttpHandle; // Handle of any active request - LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data - S32 mHttpPolicyClass; - bool mHttpActive; // Active request to http library - U32 mHttpReplySize, // Actual received data size - mHttpReplyOffset; // Actual received data offset - bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore - - // State history - U32 mCacheReadCount, - mCacheWriteCount, - mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 + + // Work Data + LLMutex mWorkMutex; + struct PacketData + { + PacketData(U8* data, S32 size) + : mData(data), mSize(size) + {} + ~PacketData() { clearData(); } + void clearData() { delete[] mData; mData = NULL; } + + U8* mData; + U32 mSize; + }; + std::vector<PacketData*> mPackets; + S32 mFirstPacket; + S32 mLastPacket; + U16 mTotalPackets; + U8 mImageCodec; + + LLViewerAssetStats::duration_t mMetricsStartTime; + + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data + S32 mHttpPolicyClass; + bool mHttpActive; // Active request to http library + U32 mHttpReplySize, // Actual received data size + mHttpReplyOffset; // Actual received data offset + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + // State history + U32 mCacheReadCount, + mCacheWriteCount, + mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -732,19 +732,19 @@ private: class LLTextureFetch::TFRequest // : public LLQueuedThread::QueuedRequest { public: - // Default ctors and assignment operator are correct. + // Default ctors and assignment operator are correct. - virtual ~TFRequest() - {} + virtual ~TFRequest() + {} - // Patterned after QueuedRequest's method but expected behavior - // is different. Always expected to complete on the first call - // and work dispatcher will assume the same and delete the - // request after invocation. - virtual bool doWork(LLTextureFetch * fetcher) = 0; + // Patterned after QueuedRequest's method but expected behavior + // is different. Always expected to complete on the first call + // and work dispatcher will assume the same and delete the + // request after invocation. + virtual bool doWork(LLTextureFetch * fetcher) = 0; }; -namespace +namespace { /** @@ -761,19 +761,19 @@ namespace class TFReqSetRegion : public LLTextureFetch::TFRequest { public: - TFReqSetRegion(U64 region_handle) - : LLTextureFetch::TFRequest(), - mRegionHandle(region_handle) - {} - TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined + TFReqSetRegion(U64 region_handle) + : LLTextureFetch::TFRequest(), + mRegionHandle(region_handle) + {} + TFReqSetRegion & operator=(const TFReqSetRegion &); // Not defined + + virtual ~TFReqSetRegion() + {} - virtual ~TFReqSetRegion() - {} + virtual bool doWork(LLTextureFetch * fetcher); - virtual bool doWork(LLTextureFetch * fetcher); - public: - const U64 mRegionHandle; + const U64 mRegionHandle; }; @@ -795,36 +795,36 @@ class TFReqSendMetrics : public LLTextureFetch::TFRequest { public: /** - * Construct the 'Send Metrics' command to have the TextureFetch - * thread add and log metrics data. - * - * @param caps_url URL of a "ViewerMetrics" Caps target - * to receive the data. Does not have to - * be associated with a particular region. - * - * @param session_id UUID of the agent's session. - * - * @param agent_id UUID of the agent. (Being pure here...) - * - * @param main_stats Pointer to a clone of the main thread's - * LLViewerAssetStats data. Thread1 takes - * ownership of the copy and disposes of it - * when done. - */ + * Construct the 'Send Metrics' command to have the TextureFetch + * thread add and log metrics data. + * + * @param caps_url URL of a "ViewerMetrics" Caps target + * to receive the data. Does not have to + * be associated with a particular region. + * + * @param session_id UUID of the agent's session. + * + * @param agent_id UUID of the agent. (Being pure here...) + * + * @param main_stats Pointer to a clone of the main thread's + * LLViewerAssetStats data. Thread1 takes + * ownership of the copy and disposes of it + * when done. + */ TFReqSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLSD& stats_sd); - TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined + TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined - virtual ~TFReqSendMetrics(); + virtual ~TFReqSendMetrics(); + + virtual bool doWork(LLTextureFetch * fetcher); - virtual bool doWork(LLTextureFetch * fetcher); - public: - const std::string mCapsURL; - const LLUUID mSessionID; - const LLUUID mAgentID; + const std::string mCapsURL; + const LLUUID mSessionID; + const LLUUID mAgentID; LLSD mStatsSD; private: @@ -835,11 +835,11 @@ private: * Examines the merged viewer metrics report and if found to be too long, * will attempt to truncate it in some reasonable fashion. * - * @param max_regions Limit of regions allowed in report. + * @param max_regions Limit of regions allowed in report. * - * @param metrics Full, merged viewer metrics report. + * @param metrics Full, merged viewer metrics report. * - * @returns If data was truncated, returns true. + * @returns If data was truncated, returns true. */ bool truncate_viewer_metrics(int max_regions, LLSD & metrics); @@ -849,212 +849,212 @@ bool truncate_viewer_metrics(int max_regions, LLSD & metrics); ////////////////////////////////////////////////////////////////////////////// const char* sStateDescs[] = { - "INVALID", - "INIT", - "LOAD_FROM_TEXTURE_CACHE", - "CACHE_POST", - "LOAD_FROM_NETWORK", - "WAIT_HTTP_RESOURCE", - "WAIT_HTTP_RESOURCE2", - "SEND_HTTP_REQ", - "WAIT_HTTP_REQ", - "DECODE_IMAGE", - "DECODE_IMAGE_UPDATE", - "WRITE_TO_CACHE", - "WAIT_ON_WRITE", - "DONE" + "INVALID", + "INIT", + "LOAD_FROM_TEXTURE_CACHE", + "CACHE_POST", + "LOAD_FROM_NETWORK", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", + "WAIT_HTTP_REQ", + "DECODE_IMAGE", + "DECODE_IMAGE_UPDATE", + "WRITE_TO_CACHE", + "WAIT_ON_WRITE", + "DONE" }; const std::set<S32> LOGGED_STATES = { LLTextureFetchWorker::LOAD_FROM_TEXTURE_CACHE, LLTextureFetchWorker::LOAD_FROM_NETWORK, - LLTextureFetchWorker::WAIT_HTTP_REQ, LLTextureFetchWorker::DECODE_IMAGE_UPDATE, LLTextureFetchWorker::WAIT_ON_WRITE }; + LLTextureFetchWorker::WAIT_HTTP_REQ, LLTextureFetchWorker::DECODE_IMAGE_UPDATE, LLTextureFetchWorker::WAIT_ON_WRITE }; // static -volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break +volatile bool LLTextureFetch::svMetricsDataBreak(true); // Start with a data break // called from MAIN THREAD LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, - FTType f_type, // Fetched image type - const std::string& url, // Optional URL - const LLUUID& id, // Image UUID - const LLHost& host, // Simulator host - F32 priority, // Priority - S32 discard, // Desired discard - S32 size) // Desired size - : LLWorkerClass(fetcher, "TextureFetch"), - LLCore::HttpHandler(), - mState(INIT), - mWriteToCacheState(NOT_WRITE), - mFetcher(fetcher), - mFTType(f_type), - mID(id), - mHost(host), - mUrl(url), - mImagePriority(priority), - mRequestedPriority(0.f), - mDesiredDiscard(-1), - mSimRequestedDiscard(-1), - mRequestedDiscard(-1), - mLoadedDiscard(-1), - mDecodedDiscard(-1), - mCacheReadTime(0.f), - mCacheWriteTime(0.f), - mDecodeTime(0.f), + FTType f_type, // Fetched image type + const std::string& url, // Optional URL + const LLUUID& id, // Image UUID + const LLHost& host, // Simulator host + F32 priority, // Priority + S32 discard, // Desired discard + S32 size) // Desired size + : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), + mState(INIT), + mWriteToCacheState(NOT_WRITE), + mFetcher(fetcher), + mFTType(f_type), + mID(id), + mHost(host), + mUrl(url), + mImagePriority(priority), + mRequestedPriority(0.f), + mDesiredDiscard(-1), + mSimRequestedDiscard(-1), + mRequestedDiscard(-1), + mLoadedDiscard(-1), + mDecodedDiscard(-1), + mCacheReadTime(0.f), + mCacheWriteTime(0.f), + mDecodeTime(0.f), mFetchTime(0.f), - mCacheReadHandle(LLTextureCache::nullHandle()), - mCacheWriteHandle(LLTextureCache::nullHandle()), - mRequestedSize(0), - mRequestedOffset(0), - mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), - mFileSize(0), - mSkippedStatesTime(0), - mCachedSize(0), - mLoaded(FALSE), - mSentRequest(UNSENT), - mDecodeHandle(0), - mDecoded(FALSE), - mWritten(FALSE), - mNeedsAux(FALSE), - mHaveAllData(FALSE), - mInLocalCache(FALSE), - mInCache(FALSE), - mCanUseHTTP(true), - mRetryAttempt(0), - mActiveCount(0), - mWorkMutex(), - mFirstPacket(0), - mLastPacket(-1), - mTotalPackets(0), - mImageCodec(IMG_CODEC_INVALID), - mMetricsStartTime(0), - mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), - mHttpBufferArray(NULL), - mHttpPolicyClass(mFetcher->mHttpPolicyClass), - mHttpActive(false), - mHttpReplySize(0U), - mHttpReplyOffset(0U), - mHttpHasResource(false), - mCacheReadCount(0U), - mCacheWriteCount(0U), - mResourceWaitCount(0U), + mCacheReadHandle(LLTextureCache::nullHandle()), + mCacheWriteHandle(LLTextureCache::nullHandle()), + mRequestedSize(0), + mRequestedOffset(0), + mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), + mFileSize(0), + mSkippedStatesTime(0), + mCachedSize(0), + mLoaded(FALSE), + mSentRequest(UNSENT), + mDecodeHandle(0), + mDecoded(FALSE), + mWritten(FALSE), + mNeedsAux(FALSE), + mHaveAllData(FALSE), + mInLocalCache(FALSE), + mInCache(FALSE), + mCanUseHTTP(true), + mRetryAttempt(0), + mActiveCount(0), + mWorkMutex(), + mFirstPacket(0), + mLastPacket(-1), + mTotalPackets(0), + mImageCodec(IMG_CODEC_INVALID), + mMetricsStartTime(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(mFetcher->mHttpPolicyClass), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U), + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U), mFetchRetryPolicy(10.f,3600.f,2.f,10), mCanUseCapability(true), mRegionRetryAttempt(0) { - mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; -// LL_INFOS(LOG_TXT) << "Create: " << mID << " mHost:" << host << " Discard=" << discard << LL_ENDL; - if (!mFetcher->mDebugPause) - { - addWork(0); - } - setDesiredDiscard(discard, size); + mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; +// LL_INFOS(LOG_TXT) << "Create: " << mID << " mHost:" << host << " Discard=" << discard << LL_ENDL; + if (!mFetcher->mDebugPause) + { + addWork(0); + } + setDesiredDiscard(discard, size); } LLTextureFetchWorker::~LLTextureFetchWorker() { -// LL_INFOS(LOG_TXT) << "Destroy: " << mID -// << " Decoded=" << mDecodedDiscard -// << " Requested=" << mRequestedDiscard -// << " Desired=" << mDesiredDiscard << LL_ENDL; - llassert_always(!haveWork()); - - lockWorkMutex(); // +Mw (should be useless) - if (mHttpHasResource) - { - // Last-chance catchall to recover the resource. Using an - // atomic datatype solely because this can be running in - // another thread. - releaseHttpSemaphore(); - } - if (mHttpActive) - { - // Issue a cancel on a live request... +// LL_INFOS(LOG_TXT) << "Destroy: " << mID +// << " Decoded=" << mDecodedDiscard +// << " Requested=" << mRequestedDiscard +// << " Desired=" << mDesiredDiscard << LL_ENDL; + llassert_always(!haveWork()); + + lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + // Last-chance catchall to recover the resource. Using an + // atomic datatype solely because this can be running in + // another thread. + releaseHttpSemaphore(); + } + if (mHttpActive) + { + // Issue a cancel on a live request... mFetcher->getHttpRequest().requestCancel(mHttpHandle, LLCore::HttpHandler::ptr_t()); - } - if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) - { - mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); - } - if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) - { - mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); - } - mFormattedImage = NULL; - clearPackets(); - if (mHttpBufferArray) - { - mHttpBufferArray->release(); - mHttpBufferArray = NULL; - } - unlockWorkMutex(); // -Mw - mFetcher->removeFromHTTPQueue(mID, (S32Bytes)0); - mFetcher->removeHttpWaiter(mID); - mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); + } + if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) + { + mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); + } + if (mCacheWriteHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) + { + mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); + } + mFormattedImage = NULL; + clearPackets(); + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID, (S32Bytes)0); + mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } // Locks: Mw void LLTextureFetchWorker::clearPackets() { - for_each(mPackets.begin(), mPackets.end(), DeletePointer()); - mPackets.clear(); - mTotalPackets = 0; - mLastPacket = -1; - mFirstPacket = 0; + for_each(mPackets.begin(), mPackets.end(), DeletePointer()); + mPackets.clear(); + mTotalPackets = 0; + mLastPacket = -1; + mFirstPacket = 0; } // Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { - bool prioritize = false; - if (mDesiredDiscard != discard) - { - if (!haveWork()) - { - if (!mFetcher->mDebugPause) - { - addWork(0); - } - } - else if (mDesiredDiscard < discard) - { - prioritize = true; - } - mDesiredDiscard = discard; - mDesiredSize = size; - } - else if (size > mDesiredSize) - { - mDesiredSize = size; - prioritize = true; - } - mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); - if ((prioritize && mState == INIT) || mState == DONE) - { - setState(INIT); - } + bool prioritize = false; + if (mDesiredDiscard != discard) + { + if (!haveWork()) + { + if (!mFetcher->mDebugPause) + { + addWork(0); + } + } + else if (mDesiredDiscard < discard) + { + prioritize = true; + } + mDesiredDiscard = discard; + mDesiredSize = size; + } + else if (size > mDesiredSize) + { + mDesiredSize = size; + prioritize = true; + } + mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); + if ((prioritize && mState == INIT) || mState == DONE) + { + setState(INIT); + } } // Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { - mImagePriority = priority; //should map to max virtual size, abort if zero + mImagePriority = priority; //should map to max virtual size, abort if zero } // Locks: Mw void LLTextureFetchWorker::resetFormattedData() { - if (mHttpBufferArray) - { - mHttpBufferArray->release(); - mHttpBufferArray = NULL; - } - if (mFormattedImage.notNull()) - { - mFormattedImage->deleteData(); - } - mHttpReplySize = 0; - mHttpReplyOffset = 0; - mHaveAllData = FALSE; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + if (mFormattedImage.notNull()) + { + mFormattedImage->deleteData(); + } + mHttpReplySize = 0; + mHttpReplyOffset = 0; + mHaveAllData = FALSE; } F32 LLTextureFetchWorker::getImagePriority() const @@ -1065,41 +1065,41 @@ F32 LLTextureFetchWorker::getImagePriority() const // Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { - llassert(mFormattedImage.isNull()); + llassert(mFormattedImage.isNull()); } // Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; - if (gNonInteractive) - { - return true; - } - static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 - static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 - static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; - - LLMutexLock lock(&mWorkMutex); // +Mw - - if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) - { - if (mState < DECODE_IMAGE) - { + if (gNonInteractive) + { + return true; + } + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; + + LLMutexLock lock(&mWorkMutex); // +Mw + + if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) + { + if (mState < DECODE_IMAGE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - state < decode"); - return true; // abort - } - } - - if (mImagePriority < F_ALMOST_ZERO) - { - if (mState == INIT || mState == LOAD_FROM_NETWORK) - { + return true; // abort + } + } + + if (mImagePriority < F_ALMOST_ZERO) + { + if (mState == INIT || mState == LOAD_FROM_NETWORK) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - priority < 0"); - LL_DEBUGS(LOG_TXT) << mID << " abort: mImagePriority < F_ALMOST_ZERO" << LL_ENDL; - return true; // abort - } - } + LL_DEBUGS(LOG_TXT) << mID << " abort: mImagePriority < F_ALMOST_ZERO" << LL_ENDL; + return true; // abort + } + } if (mState > CACHE_POST && !mCanUseCapability && mCanUseHTTP) { if (mRegionRetryAttempt > MAX_CAP_MISSING_RETRIES) @@ -1112,452 +1112,452 @@ bool LLTextureFetchWorker::doWork(S32 param) } // else retry } - if(mState > CACHE_POST && !mCanUseHTTP) - { + if(mState > CACHE_POST && !mCanUseHTTP) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - state > cache_post"); - //nowhere to get data, abort. - LL_WARNS(LOG_TXT) << mID << " abort, nowhere to get data" << LL_ENDL; - return true ; - } - - if (mFetcher->mDebugPause) - { - return false; // debug: don't do any work - } - if (mID == mFetcher->mDebugID) - { - mFetcher->mDebugCount++; // for setting breakpoints - } - - if (mState != DONE) - { - mFetchDeltaTimer.reset(); - } - - if (mState == INIT) - { + //nowhere to get data, abort. + LL_WARNS(LOG_TXT) << mID << " abort, nowhere to get data" << LL_ENDL; + return true ; + } + + if (mFetcher->mDebugPause) + { + return false; // debug: don't do any work + } + if (mID == mFetcher->mDebugID) + { + mFetcher->mDebugCount++; // for setting breakpoints + } + + if (mState != DONE) + { + mFetchDeltaTimer.reset(); + } + + if (mState == INIT) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - INIT"); - mStateTimer.reset(); - mFetchTimer.reset(); - for(auto i : LOGGED_STATES) - { - mStateTimersMap[i] = 0; - } - mSkippedStatesTime = 0; - mRawImage = NULL ; - mRequestedDiscard = -1; - mLoadedDiscard = -1; - mDecodedDiscard = -1; - mRequestedSize = 0; - mRequestedOffset = 0; - mFileSize = 0; - mCachedSize = 0; - mLoaded = FALSE; - mSentRequest = UNSENT; - mDecoded = FALSE; - mWritten = FALSE; - if (mHttpBufferArray) - { - mHttpBufferArray->release(); - mHttpBufferArray = NULL; - } - mHttpReplySize = 0; - mHttpReplyOffset = 0; - mHaveAllData = FALSE; - clearPackets(); // TODO: Shouldn't be necessary - mCacheReadHandle = LLTextureCache::nullHandle(); - mCacheWriteHandle = LLTextureCache::nullHandle(); - setState(LOAD_FROM_TEXTURE_CACHE); - mInCache = FALSE; - mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE - LL_DEBUGS(LOG_TXT) << mID << ": Priority: " << llformat("%8.0f",mImagePriority) - << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; - - // fall through - } - - if (mState == LOAD_FROM_TEXTURE_CACHE) - { + mStateTimer.reset(); + mFetchTimer.reset(); + for(auto i : LOGGED_STATES) + { + mStateTimersMap[i] = 0; + } + mSkippedStatesTime = 0; + mRawImage = NULL ; + mRequestedDiscard = -1; + mLoadedDiscard = -1; + mDecodedDiscard = -1; + mRequestedSize = 0; + mRequestedOffset = 0; + mFileSize = 0; + mCachedSize = 0; + mLoaded = FALSE; + mSentRequest = UNSENT; + mDecoded = FALSE; + mWritten = FALSE; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + mHttpReplySize = 0; + mHttpReplyOffset = 0; + mHaveAllData = FALSE; + clearPackets(); // TODO: Shouldn't be necessary + mCacheReadHandle = LLTextureCache::nullHandle(); + mCacheWriteHandle = LLTextureCache::nullHandle(); + setState(LOAD_FROM_TEXTURE_CACHE); + mInCache = FALSE; + mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE + LL_DEBUGS(LOG_TXT) << mID << ": Priority: " << llformat("%8.0f",mImagePriority) + << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; + + // fall through + } + + if (mState == LOAD_FROM_TEXTURE_CACHE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - LOAD_FROM_TEXTURE_CACHE"); - if (mCacheReadHandle == LLTextureCache::nullHandle()) - { - S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; - S32 size = mDesiredSize - offset; - if (size <= 0) - { - setState(CACHE_POST); + if (mCacheReadHandle == LLTextureCache::nullHandle()) + { + S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; + S32 size = mDesiredSize - offset; + if (size <= 0) + { + setState(CACHE_POST); return doWork(param); // return false; - } - mFileSize = 0; - mLoaded = FALSE; + } + mFileSize = 0; + mLoaded = FALSE; add(LLTextureFetch::sCacheAttempt, 1.0); - if (mUrl.compare(0, 7, "file://") == 0) - { - // read file from local disk - ++mCacheReadCount; - std::string filename = mUrl.substr(7, std::string::npos); - CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); - mCacheReadTimer.reset(); - mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, offset, size, responder); - - } - else if ((mUrl.empty() || mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache()) - { - ++mCacheReadCount; - CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); - mCacheReadTimer.reset(); - mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, - offset, size, responder);; - } - else if(!mUrl.empty() && mCanUseHTTP) - { - setState(WAIT_HTTP_RESOURCE); - } - else - { - setState(LOAD_FROM_NETWORK); - } - } - - if (mLoaded) - { - // Make sure request is complete. *TODO: make this auto-complete - if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false)) - { - mCacheReadHandle = LLTextureCache::nullHandle(); - setState(CACHE_POST); + if (mUrl.compare(0, 7, "file://") == 0) + { + // read file from local disk + ++mCacheReadCount; + std::string filename = mUrl.substr(7, std::string::npos); + CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); + mCacheReadTimer.reset(); + mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, offset, size, responder); + + } + else if ((mUrl.empty() || mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache()) + { + ++mCacheReadCount; + CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); + mCacheReadTimer.reset(); + mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, + offset, size, responder);; + } + else if(!mUrl.empty() && mCanUseHTTP) + { + setState(WAIT_HTTP_RESOURCE); + } + else + { + setState(LOAD_FROM_NETWORK); + } + } + + if (mLoaded) + { + // Make sure request is complete. *TODO: make this auto-complete + if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false)) + { + mCacheReadHandle = LLTextureCache::nullHandle(); + setState(CACHE_POST); add(LLTextureFetch::sCacheHit, 1.0); - mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); - // fall through - } - else - { - // - //This should never happen - // - LL_DEBUGS(LOG_TXT) << mID << " this should never happen" << LL_ENDL; - return false; - } - } - else - { - return false; - } - } - - if (mState == CACHE_POST) - { + mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); + // fall through + } + else + { + // + //This should never happen + // + LL_DEBUGS(LOG_TXT) << mID << " this should never happen" << LL_ENDL; + return false; + } + } + else + { + return false; + } + } + + if (mState == CACHE_POST) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - CACHE_POST"); - mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; - // Successfully loaded - if ((mCachedSize >= mDesiredSize) || mHaveAllData) - { - // we have enough data, decode it - llassert_always(mFormattedImage->getDataSize() > 0); - mLoadedDiscard = mDesiredDiscard; - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; - } - setState(DECODE_IMAGE); - mInCache = TRUE; - mWriteToCacheState = NOT_WRITE ; - LL_DEBUGS(LOG_TXT) << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() - << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) - << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; - record(LLTextureFetch::sCacheHitRate, LLUnits::Ratio::fromValue(1)); - } - else - { - if (mUrl.compare(0, 7, "file://") == 0) - { - // failed to load local file, we're done. - LL_WARNS(LOG_TXT) << mID << ": abort, failed to load local file " << mUrl << LL_ENDL; - return true; - } - // need more data - else - { - LL_DEBUGS(LOG_TXT) << mID << ": Not in Cache" << LL_ENDL; - setState(LOAD_FROM_NETWORK); - } - record(LLTextureFetch::sCacheHitRate, LLUnits::Ratio::fromValue(0)); - // fall through - } - } - - if (mState == LOAD_FROM_NETWORK) - { + mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; + // Successfully loaded + if ((mCachedSize >= mDesiredSize) || mHaveAllData) + { + // we have enough data, decode it + llassert_always(mFormattedImage->getDataSize() > 0); + mLoadedDiscard = mDesiredDiscard; + if (mLoadedDiscard < 0) + { + LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << LL_ENDL; + } + setState(DECODE_IMAGE); + mInCache = TRUE; + mWriteToCacheState = NOT_WRITE ; + LL_DEBUGS(LOG_TXT) << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() + << " Size: " << llformat("%dx%d",mFormattedImage->getWidth(),mFormattedImage->getHeight()) + << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; + record(LLTextureFetch::sCacheHitRate, LLUnits::Ratio::fromValue(1)); + } + else + { + if (mUrl.compare(0, 7, "file://") == 0) + { + // failed to load local file, we're done. + LL_WARNS(LOG_TXT) << mID << ": abort, failed to load local file " << mUrl << LL_ENDL; + return true; + } + // need more data + else + { + LL_DEBUGS(LOG_TXT) << mID << ": Not in Cache" << LL_ENDL; + setState(LOAD_FROM_NETWORK); + } + record(LLTextureFetch::sCacheHitRate, LLUnits::Ratio::fromValue(0)); + // fall through + } + } + + if (mState == LOAD_FROM_NETWORK) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - LOAD_FROM_NETWORK"); - // Check for retries to previous server failures. - F32 wait_seconds; - if (mFetchRetryPolicy.shouldRetry(wait_seconds)) - { - if (wait_seconds <= 0.0) - { - LL_INFOS(LOG_TXT) << mID << " retrying now" << LL_ENDL; - } - else - { - //LL_INFOS(LOG_TXT) << mID << " waiting to retry for " << wait_seconds << " seconds" << LL_ENDL; - return false; - } - } - - static LLCachedControl<bool> use_http(gSavedSettings, "ImagePipelineUseHTTP", true); - -// if (mHost.isInvalid()) get_url = false; - if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. - { - LLViewerRegion* region = getRegion(); - if (region) - { - std::string http_url = region->getViewerAssetUrl(); - if (!http_url.empty()) - { - if (mFTType != FTT_DEFAULT) - { + // Check for retries to previous server failures. + F32 wait_seconds; + if (mFetchRetryPolicy.shouldRetry(wait_seconds)) + { + if (wait_seconds <= 0.0) + { + LL_INFOS(LOG_TXT) << mID << " retrying now" << LL_ENDL; + } + else + { + //LL_INFOS(LOG_TXT) << mID << " waiting to retry for " << wait_seconds << " seconds" << LL_ENDL; + return false; + } + } + + static LLCachedControl<bool> use_http(gSavedSettings, "ImagePipelineUseHTTP", true); + +// if (mHost.isInvalid()) get_url = false; + if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. + { + LLViewerRegion* region = getRegion(); + if (region) + { + std::string http_url = region->getViewerAssetUrl(); + if (!http_url.empty()) + { + if (mFTType != FTT_DEFAULT) + { LL_WARNS(LOG_TXT) << "Trying to fetch a texture of non-default type by UUID. This probably won't work!" << LL_ENDL; - } - setUrl(http_url + "/?texture_id=" + mID.asString().c_str()); - LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL; - mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. + } + setUrl(http_url + "/?texture_id=" + mID.asString().c_str()); + LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL; + mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. mCanUseCapability = true; mRegionRetryAttempt = 0; mLastRegionId = region->getRegionID(); - } - else - { - mCanUseCapability = false; + } + else + { + mCanUseCapability = false; mRegionRetryAttempt++; mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY); // ex: waiting for caps - LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; - } - } - else - { + LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; + } + } + else + { mCanUseCapability = false; mRegionRetryAttempt++; mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY); - // This will happen if not logged in or if a region deoes not have HTTP Texture enabled - //LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; + // This will happen if not logged in or if a region deoes not have HTTP Texture enabled + //LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; - } - } - else if (mFTType == FTT_SERVER_BAKE) - { - mWriteToCacheState = CAN_WRITE; - } - - if (mCanUseCapability && mCanUseHTTP && !mUrl.empty()) - { - setState(WAIT_HTTP_RESOURCE); - if(mWriteToCacheState != NOT_WRITE) - { - mWriteToCacheState = CAN_WRITE ; - } - // don't return, fall through to next state - } - else - { - return false; - } - } - - if (mState == WAIT_HTTP_RESOURCE) - { + } + } + else if (mFTType == FTT_SERVER_BAKE) + { + mWriteToCacheState = CAN_WRITE; + } + + if (mCanUseCapability && mCanUseHTTP && !mUrl.empty()) + { + setState(WAIT_HTTP_RESOURCE); + if(mWriteToCacheState != NOT_WRITE) + { + mWriteToCacheState = CAN_WRITE ; + } + // don't return, fall through to next state + } + else + { + return false; + } + } + + if (mState == WAIT_HTTP_RESOURCE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - WAIT_HTTP_RESOURCE"); - // NOTE: - // control the number of the http requests issued for: - // 1, not openning too many file descriptors at the same time; - // 2, control the traffic of http so udp gets bandwidth. - // - // If it looks like we're busy, keep this request here. - // Otherwise, advance into the HTTP states. - - if (!mHttpHasResource && // sometimes we get into this state when we already have an http resource, go ahead and send the request in that case + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + + if (!mHttpHasResource && // sometimes we get into this state when we already have an http resource, go ahead and send the request in that case (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore())) - { - setState(WAIT_HTTP_RESOURCE2); - mFetcher->addHttpWaiter(this->mID); - ++mResourceWaitCount; - return false; - } - - setState(SEND_HTTP_REQ); - // *NOTE: You must invoke releaseHttpSemaphore() if you transition - // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort - // the request. - } - - if (mState == WAIT_HTTP_RESOURCE2) - { + { + setState(WAIT_HTTP_RESOURCE2); + mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; + return false; + } + + setState(SEND_HTTP_REQ); + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. + } + + if (mState == WAIT_HTTP_RESOURCE2) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - WAIT_HTTP_RESOURCE2"); - // Just idle it if we make it to the head... - return false; - } - - if (mState == SEND_HTTP_REQ) - { + // Just idle it if we make it to the head... + return false; + } + + if (mState == SEND_HTTP_REQ) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - SEND_HTTP_REQ"); - // Also used in llmeshrepository - static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); - - if (! mCanUseHTTP) - { - releaseHttpSemaphore(); - LL_WARNS(LOG_TXT) << mID << " abort: SEND_HTTP_REQ but !mCanUseHTTP" << LL_ENDL; - return true; // abort - } - - S32 cur_size = 0; - if (mFormattedImage.notNull()) - { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) - { - if (cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; - } - setState(DECODE_IMAGE); - releaseHttpSemaphore(); - //return false; + // Also used in llmeshrepository + static LLCachedControl<bool> disable_range_req(gSavedSettings, "HttpRangeRequestsDisable", false); + + if (! mCanUseHTTP) + { + releaseHttpSemaphore(); + LL_WARNS(LOG_TXT) << mID << " abort: SEND_HTTP_REQ but !mCanUseHTTP" << LL_ENDL; + return true; // abort + } + + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) + { + if (cur_size > 0) + { + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + if (mLoadedDiscard < 0) + { + LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << LL_ENDL; + } + setState(DECODE_IMAGE); + releaseHttpSemaphore(); + //return false; return doWork(param); - } - else - { - releaseHttpSemaphore(); - LL_WARNS(LOG_TXT) << mID << " SEND_HTTP_REQ abort: cur_size " << cur_size << " <=0" << LL_ENDL; - return true; // abort. - } - } - } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - mRequestedOffset = cur_size; - if (mRequestedOffset) - { - // Texture fetching often issues 'speculative' loads that - // start beyond the end of the actual asset. Some cache/web - // systems, e.g. Varnish, will respond to this not with a - // 416 but with a 200 and the entire asset in the response - // body. By ensuring that we always have a partially - // satisfiable Range request, we avoid that hit to the network. - // We just have to deal with the overlapping data which is made - // somewhat harder by the fact that grid services don't necessarily - // return the Content-Range header on 206 responses. *Sigh* - mRequestedOffset -= 1; - mRequestedSize += 1; - } - mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; - - if (mUrl.empty()) - { - // *FIXME: This should not be reachable except it has become - // so after some recent 'work'. Need to track this down - // and illuminate the unenlightened. - LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID - << " on empty URL." << LL_ENDL; - resetFormattedData(); - releaseHttpSemaphore(); - return true; // failed - } - - mRequestedDeltaTimer.reset(); - mLoaded = FALSE; - mGetStatus = LLCore::HttpStatus(); - mGetReason.clear(); - LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - - // Will call callbackHttpGet when curl request completes - // Only server bake images use the returned headers currently, for getting retry-after field. - LLCore::HttpOptions::ptr_t options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; - if (disable_range_req) - { - // 'Range:' requests may be disabled in which case all HTTP - // texture fetches result in full fetches. This can be used - // by people with questionable ISPs or networking gear that - // doesn't handle these well. - mHttpHandle = mFetcher->mHttpRequest->requestGet(mHttpPolicyClass, - mUrl, - options, - mFetcher->mHttpHeaders, + } + else + { + releaseHttpSemaphore(); + LL_WARNS(LOG_TXT) << mID << " SEND_HTTP_REQ abort: cur_size " << cur_size << " <=0" << LL_ENDL; + return true; // abort. + } + } + } + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + if (mRequestedOffset) + { + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; + } + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + + if (mUrl.empty()) + { + // *FIXME: This should not be reachable except it has become + // so after some recent 'work'. Need to track this down + // and illuminate the unenlightened. + LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID + << " on empty URL." << LL_ENDL; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed + } + + mRequestedDeltaTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS(LOG_TXT) << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + // Only server bake images use the returned headers currently, for getting retry-after field. + LLCore::HttpOptions::ptr_t options = (mFTType == FTT_SERVER_BAKE) ? mFetcher->mHttpOptionsWithHeaders: mFetcher->mHttpOptions; + if (disable_range_req) + { + // 'Range:' requests may be disabled in which case all HTTP + // texture fetches result in full fetches. This can be used + // by people with questionable ISPs or networking gear that + // doesn't handle these well. + mHttpHandle = mFetcher->mHttpRequest->requestGet(mHttpPolicyClass, + mUrl, + options, + mFetcher->mHttpHeaders, LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); - } - else - { - mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, - mUrl, - mRequestedOffset, - (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX - ? 0 - : mRequestedSize, - options, - mFetcher->mHttpHeaders, + } + else + { + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mUrl, + mRequestedOffset, + (mRequestedOffset + mRequestedSize) > HTTP_REQUESTS_RANGE_END_MAX + ? 0 + : mRequestedSize, + options, + mFetcher->mHttpHeaders, LLCore::HttpHandler::ptr_t(this, &NoOpDeletor)); - } - if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) - { - LLCore::HttpStatus status(mFetcher->mHttpRequest->getStatus()); - LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID - << ", Status: " << status.toTerseString() - << " Reason: '" << status.toString() << "'" - << LL_ENDL; - resetFormattedData(); - releaseHttpSemaphore(); - return true; // failed - } - - mHttpActive = true; - mFetcher->addToHTTPQueue(mID); - recordTextureStart(true); - setState(WAIT_HTTP_REQ); - - // fall through - } - - if (mState == WAIT_HTTP_REQ) - { + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + LLCore::HttpStatus status(mFetcher->mHttpRequest->getStatus()); + LL_WARNS(LOG_TXT) << "HTTP GET request failed for " << mID + << ", Status: " << status.toTerseString() + << " Reason: '" << status.toString() << "'" + << LL_ENDL; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed + } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setState(WAIT_HTTP_REQ); + + // fall through + } + + if (mState == WAIT_HTTP_REQ) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - WAIT_HTTP_REQ"); - // *NOTE: As stated above, all transitions out of this state should - // call releaseHttpSemaphore(). - if (mLoaded) - { - S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; - if (mRequestedSize < 0) - { - if (http_not_found == mGetStatus) - { - if (mFTType != FTT_MAP_TILE) - { - LL_WARNS(LOG_TXT) << "Texture missing from server (404): " << mUrl << LL_ENDL; - } - - if(mWriteToCacheState == NOT_WRITE) //map tiles or server bakes - { - setState(DONE); - releaseHttpSemaphore(); - if (mFTType != FTT_MAP_TILE) - { - LL_WARNS(LOG_TXT) << mID << " abort: WAIT_HTTP_REQ not found" << LL_ENDL; - } - return true; - } + // *NOTE: As stated above, all transitions out of this state should + // call releaseHttpSemaphore(). + if (mLoaded) + { + S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; + if (mRequestedSize < 0) + { + if (http_not_found == mGetStatus) + { + if (mFTType != FTT_MAP_TILE) + { + LL_WARNS(LOG_TXT) << "Texture missing from server (404): " << mUrl << LL_ENDL; + } + + if(mWriteToCacheState == NOT_WRITE) //map tiles or server bakes + { + setState(DONE); + releaseHttpSemaphore(); + if (mFTType != FTT_MAP_TILE) + { + LL_WARNS(LOG_TXT) << mID << " abort: WAIT_HTTP_REQ not found" << LL_ENDL; + } + return true; + } if (mCanUseHTTP && !mUrl.empty() && cur_size <= 0) { @@ -1572,10 +1572,10 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } } - } - else if (http_service_unavail == mGetStatus) - { - LL_INFOS_ONCE(LOG_TXT) << "Texture server busy (503): " << mUrl << LL_ENDL; + } + else if (http_service_unavail == mGetStatus) + { + LL_INFOS_ONCE(LOG_TXT) << "Texture server busy (503): " << mUrl << LL_ENDL; if (mCanUseHTTP && !mUrl.empty() && cur_size <= 0) { LLViewerRegion* region = getRegion(); @@ -1589,217 +1589,217 @@ bool LLTextureFetchWorker::doWork(S32 param) return false; } } - } - else if (http_not_sat == mGetStatus) - { - // Allowed, we'll accept whatever data we have as complete. - mHaveAllData = TRUE; - } - else - { - LL_INFOS(LOG_TXT) << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus.toTerseString() - << " Reason: '" << mGetReason << "'" - << LL_ENDL; - } + } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; + } + else + { + LL_INFOS(LOG_TXT) << "HTTP GET failed for: " << mUrl + << " Status: " << mGetStatus.toTerseString() + << " Reason: '" << mGetReason << "'" + << LL_ENDL; + } if (mFTType != FTT_SERVER_BAKE && mFTType != FTT_MAP_TILE) - { - mUrl.clear(); - } - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; - } - setState(DECODE_IMAGE); - releaseHttpSemaphore(); - //return false; + { + mUrl.clear(); + } + if (cur_size > 0) + { + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + if (mLoadedDiscard < 0) + { + LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << LL_ENDL; + } + setState(DECODE_IMAGE); + releaseHttpSemaphore(); + //return false; return doWork(param); - } - - // Fail harder - resetFormattedData(); - setState(DONE); - releaseHttpSemaphore(); - LL_WARNS(LOG_TXT) << mID << " abort: fail harder" << LL_ENDL; - return true; // failed - } - - // Clear the url since we're done with the fetch - // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch - // next time the texture is requested, even if the data have already been fetched. - if(mWriteToCacheState != NOT_WRITE && mFTType != FTT_SERVER_BAKE) - { - // Why do we want to keep url if NOT_WRITE - is this a proxy for map tiles? - mUrl.clear(); - } - - if (! mHttpBufferArray || ! mHttpBufferArray->size()) - { - // no data received. - if (mHttpBufferArray) - { - mHttpBufferArray->release(); - mHttpBufferArray = NULL; - } - - // abort. - setState(DONE); - LL_WARNS(LOG_TXT) << mID << " abort: no data received" << LL_ENDL; - releaseHttpSemaphore(); - return true; - } - - S32 append_size(mHttpBufferArray->size()); - S32 total_size(cur_size + append_size); - S32 src_offset(0); - llassert_always(append_size == mRequestedSize); - if (mHttpReplyOffset && mHttpReplyOffset != cur_size) - { - // In case of a partial response, our offset may - // not be trivially contiguous with the data we have. - // Get back into alignment. - if ( (mHttpReplyOffset > cur_size) || (cur_size > mHttpReplyOffset + append_size)) - { - LL_WARNS(LOG_TXT) << "Partial HTTP response produces break in image data for texture " - << mID << ". Aborting load." << LL_ENDL; - setState(DONE); - releaseHttpSemaphore(); - return true; - } - src_offset = cur_size - mHttpReplyOffset; - append_size -= src_offset; - total_size -= src_offset; - mRequestedSize -= src_offset; // Make requested values reflect useful part - mRequestedOffset += src_offset; - } - - U8 * buffer = (U8 *)ll_aligned_malloc_16(total_size); - if (!buffer) - { - // abort. If we have no space for packet, we have not enough space to decode image - setState(DONE); - LL_WARNS(LOG_TXT) << mID << " abort: out of memory" << LL_ENDL; - releaseHttpSemaphore(); - return true; - } - - if (mFormattedImage.isNull()) - { - // For now, create formatted image based on extension - std::string extension = gDirUtilp->getExtension(mUrl); - mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); - if (mFormattedImage.isNull()) - { - mFormattedImage = new LLImageJ2C; // default - } - } - - if (mHaveAllData) //the image file is fully loaded. - { - mFileSize = total_size; - } - else //the file size is unknown. - { - mFileSize = total_size + 1 ; //flag the file is not fully loaded. - } - - if (cur_size > 0) - { - // Copy previously collected data into buffer - memcpy(buffer, mFormattedImage->getData(), cur_size); - } - mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); - - // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, total_size); - - // Done with buffer array - mHttpBufferArray->release(); - mHttpBufferArray = NULL; - mHttpReplySize = 0; - mHttpReplyOffset = 0; - - mLoadedDiscard = mRequestedDiscard; - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard - << ", should be >=0" << LL_ENDL; - } - setState(DECODE_IMAGE); - if (mWriteToCacheState != NOT_WRITE) - { - mWriteToCacheState = SHOULD_WRITE ; - } - releaseHttpSemaphore(); - //return false; + } + + // Fail harder + resetFormattedData(); + setState(DONE); + releaseHttpSemaphore(); + LL_WARNS(LOG_TXT) << mID << " abort: fail harder" << LL_ENDL; + return true; // failed + } + + // Clear the url since we're done with the fetch + // Note: mUrl is used to check is fetching is required so failure to clear it will force an http fetch + // next time the texture is requested, even if the data have already been fetched. + if(mWriteToCacheState != NOT_WRITE && mFTType != FTT_SERVER_BAKE) + { + // Why do we want to keep url if NOT_WRITE - is this a proxy for map tiles? + mUrl.clear(); + } + + if (! mHttpBufferArray || ! mHttpBufferArray->size()) + { + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + + // abort. + setState(DONE); + LL_WARNS(LOG_TXT) << mID << " abort: no data received" << LL_ENDL; + releaseHttpSemaphore(); + return true; + } + + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); + llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if ( (mHttpReplyOffset > cur_size) || (cur_size > mHttpReplyOffset + append_size)) + { + LL_WARNS(LOG_TXT) << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + setState(DONE); + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } + + U8 * buffer = (U8 *)ll_aligned_malloc_16(total_size); + if (!buffer) + { + // abort. If we have no space for packet, we have not enough space to decode image + setState(DONE); + LL_WARNS(LOG_TXT) << mID << " abort: out of memory" << LL_ENDL; + releaseHttpSemaphore(); + return true; + } + + if (mFormattedImage.isNull()) + { + // For now, create formatted image based on extension + std::string extension = gDirUtilp->getExtension(mUrl); + mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (mFormattedImage.isNull()) + { + mFormattedImage = new LLImageJ2C; // default + } + } + + if (mHaveAllData) //the image file is fully loaded. + { + mFileSize = total_size; + } + else //the file size is unknown. + { + mFileSize = total_size + 1 ; //flag the file is not fully loaded. + } + + if (cur_size > 0) + { + // Copy previously collected data into buffer + memcpy(buffer, mFormattedImage->getData(), cur_size); + } + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); + + // NOTE: setData releases current data and owns new data (buffer) + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; + + mLoadedDiscard = mRequestedDiscard; + if (mLoadedDiscard < 0) + { + LL_WARNS(LOG_TXT) << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << LL_ENDL; + } + setState(DECODE_IMAGE); + if (mWriteToCacheState != NOT_WRITE) + { + mWriteToCacheState = SHOULD_WRITE ; + } + releaseHttpSemaphore(); + //return false; return doWork(param); - } - else - { - // *HISTORY: There was a texture timeout test here originally that - // would cancel a request that was over 120 seconds old. That's - // probably not a good idea. Particularly rich regions can take - // an enormous amount of time to load textures. We'll revisit the - // various possible timeout components (total request time, connection - // time, I/O time, with and without retries, etc.) in the future. - - return false; - } - } - - if (mState == DECODE_IMAGE) - { + } + else + { + // *HISTORY: There was a texture timeout test here originally that + // would cancel a request that was over 120 seconds old. That's + // probably not a good idea. Particularly rich regions can take + // an enormous amount of time to load textures. We'll revisit the + // various possible timeout components (total request time, connection + // time, I/O time, with and without retries, etc.) in the future. + + return false; + } + } + + if (mState == DECODE_IMAGE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - DECODE_IMAGE"); - static LLCachedControl<bool> textures_decode_disabled(gSavedSettings, "TextureDecodeDisabled", false); - - if (textures_decode_disabled) - { - // for debug use, don't decode - setState(DONE); - return true; - } - - if (mDesiredDiscard < 0) - { - // We aborted, don't decode - setState(DONE); - LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: desired discard " << mDesiredDiscard << "<0" << LL_ENDL; - return true; - } - - if (mFormattedImage->getDataSize() <= 0) - { - LL_WARNS(LOG_TXT) << "Decode entered with invalid mFormattedImage. ID = " << mID << LL_ENDL; - - //abort, don't decode - setState(DONE); - LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: (mFormattedImage->getDataSize() <= 0)" << LL_ENDL; - return true; - } - if (mLoadedDiscard < 0) - { - LL_WARNS(LOG_TXT) << "Decode entered with invalid mLoadedDiscard. ID = " << mID << LL_ENDL; - - //abort, don't decode - setState(DONE); - LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL; - return true; - } - mDecodeTimer.reset(); - mRawImage = NULL; - mAuxImage = NULL; - llassert_always(mFormattedImage.notNull()); - S32 discard = mHaveAllData ? 0 : mLoadedDiscard; - mDecoded = FALSE; - setState(DECODE_IMAGE_UPDATE); - LL_DEBUGS(LOG_TXT) << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard - << " All Data: " << mHaveAllData << LL_ENDL; + static LLCachedControl<bool> textures_decode_disabled(gSavedSettings, "TextureDecodeDisabled", false); + + if (textures_decode_disabled) + { + // for debug use, don't decode + setState(DONE); + return true; + } + + if (mDesiredDiscard < 0) + { + // We aborted, don't decode + setState(DONE); + LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: desired discard " << mDesiredDiscard << "<0" << LL_ENDL; + return true; + } + + if (mFormattedImage->getDataSize() <= 0) + { + LL_WARNS(LOG_TXT) << "Decode entered with invalid mFormattedImage. ID = " << mID << LL_ENDL; + + //abort, don't decode + setState(DONE); + LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: (mFormattedImage->getDataSize() <= 0)" << LL_ENDL; + return true; + } + if (mLoadedDiscard < 0) + { + LL_WARNS(LOG_TXT) << "Decode entered with invalid mLoadedDiscard. ID = " << mID << LL_ENDL; + + //abort, don't decode + setState(DONE); + LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << LL_ENDL; + return true; + } + mDecodeTimer.reset(); + mRawImage = NULL; + mAuxImage = NULL; + llassert_always(mFormattedImage.notNull()); + S32 discard = mHaveAllData ? 0 : mLoadedDiscard; + mDecoded = FALSE; + setState(DECODE_IMAGE_UPDATE); + LL_DEBUGS(LOG_TXT) << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard + << " All Data: " << mHaveAllData << LL_ENDL; // In case worked manages to request decode, be shut down, // then init and request decode again with first decode @@ -1816,226 +1816,226 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_DEBUGS(LOG_TXT) << mID << " DECODE_IMAGE abort: failed to post for decoding" << LL_ENDL; return true; } - // fall though - } - - if (mState == DECODE_IMAGE_UPDATE) - { + // fall though + } + + if (mState == DECODE_IMAGE_UPDATE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - DECODE_IMAGE_UPDATE"); - if (mDecoded) - { + if (mDecoded) + { mDecodeTime = mDecodeTimer.getElapsedTimeF32(); - if (mDecodedDiscard < 0) - { - if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0) - { - // Cache file should be deleted, try again - LL_DEBUGS(LOG_TXT) << mID << ": Decode of cached file failed (removed), retrying" << LL_ENDL; - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; - ++mRetryAttempt; - setState(INIT); - //return false; + if (mDecodedDiscard < 0) + { + if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0) + { + // Cache file should be deleted, try again + LL_DEBUGS(LOG_TXT) << mID << ": Decode of cached file failed (removed), retrying" << LL_ENDL; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; + ++mRetryAttempt; + setState(INIT); + //return false; return doWork(param); - } - else - { - LL_DEBUGS(LOG_TXT) << "Failed to Decode image " << mID << " after " << mRetryAttempt << " retries" << LL_ENDL; - setState(DONE); // failed - } - } - else - { - llassert_always(mRawImage.notNull()); - LL_DEBUGS(LOG_TXT) << mID << ": Decoded. Discard: " << mDecodedDiscard - << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; - setState(WRITE_TO_CACHE); - } - // fall through - } - else - { - return false; - } - } - - if (mState == WRITE_TO_CACHE) - { + } + else + { + LL_DEBUGS(LOG_TXT) << "Failed to Decode image " << mID << " after " << mRetryAttempt << " retries" << LL_ENDL; + setState(DONE); // failed + } + } + else + { + llassert_always(mRawImage.notNull()); + LL_DEBUGS(LOG_TXT) << mID << ": Decoded. Discard: " << mDecodedDiscard + << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; + setState(WRITE_TO_CACHE); + } + // fall through + } + else + { + return false; + } + } + + if (mState == WRITE_TO_CACHE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - WRITE_TO_CACHE"); - if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) - { - // If we're in a local cache or we didn't actually receive any new data, - // or we failed to load anything, skip - setState(DONE); - //return false; + if (mWriteToCacheState != SHOULD_WRITE || mFormattedImage.isNull()) + { + // If we're in a local cache or we didn't actually receive any new data, + // or we failed to load anything, skip + setState(DONE); + //return false; return doWork(param); - } - S32 datasize = mFormattedImage->getDataSize(); - if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed. - { - if(mHaveAllData) - { - mFileSize = datasize ; - } - else - { - mFileSize = datasize + 1 ; //flag not fully loaded. - } - } - llassert_always(datasize); - mWritten = FALSE; - setState(WAIT_ON_WRITE); - ++mCacheWriteCount; - CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); + } + S32 datasize = mFormattedImage->getDataSize(); + if(mFileSize < datasize)//This could happen when http fetching and sim fetching mixed. + { + if(mHaveAllData) + { + mFileSize = datasize ; + } + else + { + mFileSize = datasize + 1 ; //flag not fully loaded. + } + } + llassert_always(datasize); + mWritten = FALSE; + setState(WAIT_ON_WRITE); + ++mCacheWriteCount; + CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); // This call might be under work mutex, but mRawImage is not nessesary safe here. // If something retrieves it via getRequestFinished() and modifies, image won't // be protected by work mutex and won't be safe to use here nor in cache worker. // So make sure users of getRequestFinished() does not attempt to modify image while // fetcher is working - mCacheWriteTimer.reset(); - mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, - mFormattedImage->getData(), datasize, - mFileSize, mRawImage, mDecodedDiscard, responder); - // fall through - } - - if (mState == WAIT_ON_WRITE) - { + mCacheWriteTimer.reset(); + mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, + mFormattedImage->getData(), datasize, + mFileSize, mRawImage, mDecodedDiscard, responder); + // fall through + } + + if (mState == WAIT_ON_WRITE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - WAIT_ON_WRITE"); - if (writeToCacheComplete()) - { - mCacheWriteTime = mCacheWriteTimer.getElapsedTimeF32(); - setState(DONE); - // fall through - } - else - { - if (mDesiredDiscard < mDecodedDiscard) - { - // We're waiting for this write to complete before we can receive more data - // (we can't touch mFormattedImage until the write completes) - // Prioritize the write - mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle); - } - return false; - } - } - - if (mState == DONE) - { + if (writeToCacheComplete()) + { + mCacheWriteTime = mCacheWriteTimer.getElapsedTimeF32(); + setState(DONE); + // fall through + } + else + { + if (mDesiredDiscard < mDecodedDiscard) + { + // We're waiting for this write to complete before we can receive more data + // (we can't touch mFormattedImage until the write completes) + // Prioritize the write + mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle); + } + return false; + } + } + + if (mState == DONE) + { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - DONE"); - if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) - { - // More data was requested, return to INIT - setState(INIT); - LL_DEBUGS(LOG_TXT) << mID << " more data requested, returning to INIT: " - << " mDecodedDiscard " << mDecodedDiscard << ">= 0 && mDesiredDiscard " << mDesiredDiscard - << "<" << " mDecodedDiscard " << mDecodedDiscard << LL_ENDL; - // return false; + if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) + { + // More data was requested, return to INIT + setState(INIT); + LL_DEBUGS(LOG_TXT) << mID << " more data requested, returning to INIT: " + << " mDecodedDiscard " << mDecodedDiscard << ">= 0 && mDesiredDiscard " << mDesiredDiscard + << "<" << " mDecodedDiscard " << mDecodedDiscard << LL_ENDL; + // return false; return doWork(param); - } - else - { + } + else + { mFetchTime = mFetchTimer.getElapsedTimeF32(); - return true; - } - } - - return false; -} // -Mw + return true; + } + } + + return false; +} // -Mw // Threads: Ttf // virtual void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) { LL_PROFILE_ZONE_SCOPED; - static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog", false); - static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator", false); - static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic", false) ; - - LLMutexLock lock(&mWorkMutex); // +Mw - - mHttpActive = false; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime.value()); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, LLTimer::getTotalTime()); - } - - static LLCachedControl<F32> fake_failure_rate(gSavedSettings, "TextureFetchFakeFailureRate", 0.0f); - F32 rand_val = ll_frand(); - F32 rate = fake_failure_rate; - if (mFTType == FTT_SERVER_BAKE && (fake_failure_rate > 0.0) && (rand_val < fake_failure_rate)) - { - LL_WARNS(LOG_TXT) << mID << " for debugging, setting fake failure status for texture " << mID - << " (rand was " << rand_val << "/" << rate << ")" << LL_ENDL; - response->setStatus(LLCore::HttpStatus(503)); - } - bool success = true; - bool partial = false; - LLCore::HttpStatus status(response->getStatus()); - if (!status && (mFTType == FTT_SERVER_BAKE)) - { - LL_INFOS(LOG_TXT) << mID << " state " << e_state_name[mState] << LL_ENDL; - mFetchRetryPolicy.onFailure(response); - F32 retry_after; - if (mFetchRetryPolicy.shouldRetry(retry_after)) - { - LL_INFOS(LOG_TXT) << mID << " will retry after " << retry_after << " seconds, resetting state to LOAD_FROM_NETWORK" << LL_ENDL; - mFetcher->removeFromHTTPQueue(mID, S32Bytes(0)); - std::string reason(status.toString()); - setGetStatus(status, reason); - releaseHttpSemaphore(); - setState(LOAD_FROM_NETWORK); - return; - } - else - { - LL_INFOS(LOG_TXT) << mID << " will not retry" << LL_ENDL; - } - } - else - { - mFetchRetryPolicy.onSuccess(); - } - - std::string reason(status.toString()); - setGetStatus(status, reason); - LL_DEBUGS(LOG_TXT) << "HTTP COMPLETE: " << mID - << " status: " << status.toTerseString() - << " '" << reason << "'" - << LL_ENDL; - - if (! status) - { - success = false; - if (mFTType != FTT_MAP_TILE) // missing map tiles are normal, don't complain about them. - { - LL_WARNS(LOG_TXT) << "CURL GET FAILED, status: " << status.toTerseString() - << " reason: " << reason << LL_ENDL; - } - } - else - { - // A warning about partial (HTTP 206) data. Some grid services - // do *not* return a 'Content-Range' header in the response to - // Range requests with a 206 status. We're forced to assume - // we get what we asked for in these cases until we can fix - // the services. - static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); - - partial = (par_status == status); - } - - S32BytesImplicit data_size = callbackHttpGet(response, partial, success); - - if (log_texture_traffic && data_size > 0) - { + static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog", false); + static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator", false); + static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic", false) ; + + LLMutexLock lock(&mWorkMutex); // +Mw + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime.value()); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, LLTimer::getTotalTime()); + } + + static LLCachedControl<F32> fake_failure_rate(gSavedSettings, "TextureFetchFakeFailureRate", 0.0f); + F32 rand_val = ll_frand(); + F32 rate = fake_failure_rate; + if (mFTType == FTT_SERVER_BAKE && (fake_failure_rate > 0.0) && (rand_val < fake_failure_rate)) + { + LL_WARNS(LOG_TXT) << mID << " for debugging, setting fake failure status for texture " << mID + << " (rand was " << rand_val << "/" << rate << ")" << LL_ENDL; + response->setStatus(LLCore::HttpStatus(503)); + } + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + if (!status && (mFTType == FTT_SERVER_BAKE)) + { + LL_INFOS(LOG_TXT) << mID << " state " << e_state_name[mState] << LL_ENDL; + mFetchRetryPolicy.onFailure(response); + F32 retry_after; + if (mFetchRetryPolicy.shouldRetry(retry_after)) + { + LL_INFOS(LOG_TXT) << mID << " will retry after " << retry_after << " seconds, resetting state to LOAD_FROM_NETWORK" << LL_ENDL; + mFetcher->removeFromHTTPQueue(mID, S32Bytes(0)); + std::string reason(status.toString()); + setGetStatus(status, reason); + releaseHttpSemaphore(); + setState(LOAD_FROM_NETWORK); + return; + } + else + { + LL_INFOS(LOG_TXT) << mID << " will not retry" << LL_ENDL; + } + } + else + { + mFetchRetryPolicy.onSuccess(); + } + + std::string reason(status.toString()); + setGetStatus(status, reason); + LL_DEBUGS(LOG_TXT) << "HTTP COMPLETE: " << mID + << " status: " << status.toTerseString() + << " '" << reason << "'" + << LL_ENDL; + + if (! status) + { + success = false; + if (mFTType != FTT_MAP_TILE) // missing map tiles are normal, don't complain about them. + { + LL_WARNS(LOG_TXT) << "CURL GET FAILED, status: " << status.toTerseString() + << " reason: " << reason << LL_ENDL; + } + } + else + { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + partial = (par_status == status); + } + + S32BytesImplicit data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { // one worker per multiple textures std::vector<LLViewerTexture*> textures; LLViewerTextureManager::findTextures(mID, textures); @@ -2048,24 +2048,24 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size; } } - } + } + + mFetcher->removeFromHTTPQueue(mID, data_size); - mFetcher->removeFromHTTPQueue(mID, data_size); - - recordTextureDone(true, data_size); -} // -Mw + recordTextureDone(true, data_size); +} // -Mw // Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { - LL_PROFILE_ZONE_SCOPED; - if (mDecodeHandle != 0) - { - // LL::ThreadPool has no operation to cancel a particular work item - mDecodeHandle = 0; - } - mFormattedImage = NULL; + LL_PROFILE_ZONE_SCOPED; + if (mDecodeHandle != 0) + { + // LL::ThreadPool has no operation to cancel a particular work item + mDecodeHandle = 0; + } + mFormattedImage = NULL; } ////////////////////////////////////////////////////////////////////////////// @@ -2076,91 +2076,91 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) void LLTextureFetchWorker::finishWork(S32 param, bool completed) { LL_PROFILE_ZONE_SCOPED; - // The following are required in case the work was aborted - if (mCacheReadHandle != LLTextureCache::nullHandle()) - { - mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); - mCacheReadHandle = LLTextureCache::nullHandle(); - } - if (mCacheWriteHandle != LLTextureCache::nullHandle()) - { - mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); - mCacheWriteHandle = LLTextureCache::nullHandle(); - } + // The following are required in case the work was aborted + if (mCacheReadHandle != LLTextureCache::nullHandle()) + { + mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); + mCacheReadHandle = LLTextureCache::nullHandle(); + } + if (mCacheWriteHandle != LLTextureCache::nullHandle()) + { + mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); + mCacheWriteHandle = LLTextureCache::nullHandle(); + } } // LLQueuedThread's update() method is asking if it's okay to // delete this worker. You'll notice we're not locking in here // which is a slight concern. Caller is expected to have made -// this request 'quiet' by whatever means... +// this request 'quiet' by whatever means... // // Threads: Tmain // virtual bool LLTextureFetchWorker::deleteOK() { - bool delete_ok = true; - - if (mHttpActive) - { - // HTTP library has a pointer to this worker - // and will dereference it to do notification. - delete_ok = false; - } - - if (WAIT_HTTP_RESOURCE2 == mState) - { - if (mFetcher->isHttpWaiter(mID)) - { - // Don't delete the worker out from under the releaseHttpWaiters() - // method. Keep the pointers valid, clean up after that method - // has recognized the cancelation and removed the UUID from the - // waiter list. - delete_ok = false; - } - } - - // Allow any pending reads or writes to complete - if (mCacheReadHandle != LLTextureCache::nullHandle()) - { - if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) - { - mCacheReadHandle = LLTextureCache::nullHandle(); - } - else - { - delete_ok = false; - } - } - if (mCacheWriteHandle != LLTextureCache::nullHandle()) - { - if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) - { - mCacheWriteHandle = LLTextureCache::nullHandle(); - } - else - { - delete_ok = false; - } - } - - if ((haveWork() && - // not ok to delete from these states - ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) - { - delete_ok = false; - } - - return delete_ok; + bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + + if (WAIT_HTTP_RESOURCE2 == mState) + { + if (mFetcher->isHttpWaiter(mID)) + { + // Don't delete the worker out from under the releaseHttpWaiters() + // method. Keep the pointers valid, clean up after that method + // has recognized the cancelation and removed the UUID from the + // waiter list. + delete_ok = false; + } + } + + // Allow any pending reads or writes to complete + if (mCacheReadHandle != LLTextureCache::nullHandle()) + { + if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) + { + mCacheReadHandle = LLTextureCache::nullHandle(); + } + else + { + delete_ok = false; + } + } + if (mCacheWriteHandle != LLTextureCache::nullHandle()) + { + if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) + { + mCacheWriteHandle = LLTextureCache::nullHandle(); + } + else + { + delete_ok = false; + } + } + + if ((haveWork() && + // not ok to delete from these states + ((mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) + { + delete_ok = false; + } + + return delete_ok; } // Threads: Ttf void LLTextureFetchWorker::removeFromCache() { - if (!mInLocalCache) - { - mFetcher->mTextureCache->removeFromCache(mID); - } + if (!mInLocalCache) + { + mFetcher->mTextureCache->removeFromCache(mID); + } } @@ -2169,163 +2169,163 @@ void LLTextureFetchWorker::removeFromCache() // Threads: Ttf // Locks: Mw S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, - bool partial, bool success) -{ - S32 data_size = 0 ; - - if (mState != WAIT_HTTP_REQ) - { - LL_WARNS(LOG_TXT) << "callbackHttpGet for unrequested fetch worker: " << mID - << " req=" << mSentRequest << " state= " << mState << LL_ENDL; - return data_size; - } - if (mLoaded) - { - LL_WARNS(LOG_TXT) << "Duplicate callback for " << mID.asString() << LL_ENDL; - return data_size ; // ignore duplicate callback - } - if (success) - { - // get length of stream: - LLCore::BufferArray * body(response->getBody()); - data_size = body ? body->size() : 0; - - LL_DEBUGS(LOG_TXT) << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; - if (data_size > 0) - { - // *TODO: set the formatted image data here directly to avoid the copy - - // Hold on to body for later copy - llassert_always(NULL == mHttpBufferArray); - body->addRef(); - mHttpBufferArray = body; - - if (partial) - { - unsigned int offset(0), length(0), full_length(0); - response->getRange(&offset, &length, &full_length); - if (! offset && ! length) - { - // This is the case where we receive a 206 status but - // there wasn't a useful Content-Range header in the response. - // This could be because it was badly formatted but is more - // likely due to capabilities services which scrub headers - // from responses. Assume we got what we asked for... - mHttpReplySize = data_size; - mHttpReplyOffset = mRequestedOffset; - } - else - { - mHttpReplySize = length; - mHttpReplyOffset = offset; - } - } - - if (! partial) - { - // Response indicates this is the entire asset regardless - // of our asking for a byte range. Mark it so and drop - // any partial data we might have so that the current - // response body becomes the entire dataset. - if (data_size <= mRequestedOffset) - { - LL_WARNS(LOG_TXT) << "Fetched entire texture " << mID - << " when it was expected to be marked complete. mImageSize: " - << mFileSize << " datasize: " << mFormattedImage->getDataSize() - << LL_ENDL; - } - mHaveAllData = TRUE; - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; // discard any previous data we had - } - else if (data_size < mRequestedSize) - { - mHaveAllData = TRUE; - } - else if (data_size > mRequestedSize) - { - // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) - LL_WARNS(LOG_TXT) << "data_size = " << data_size << " > requested: " << mRequestedSize << LL_ENDL; - mHaveAllData = TRUE; - llassert_always(mDecodeHandle == 0); - mFormattedImage = NULL; // discard any previous data we had - } - } - else - { - // We requested data but received none (and no error), - // so presumably we have all of it - mHaveAllData = TRUE; - } - mRequestedSize = data_size; - - if (mHaveAllData) + bool partial, bool success) +{ + S32 data_size = 0 ; + + if (mState != WAIT_HTTP_REQ) + { + LL_WARNS(LOG_TXT) << "callbackHttpGet for unrequested fetch worker: " << mID + << " req=" << mSentRequest << " state= " << mState << LL_ENDL; + return data_size; + } + if (mLoaded) + { + LL_WARNS(LOG_TXT) << "Duplicate callback for " << mID.asString() << LL_ENDL; + return data_size ; // ignore duplicate callback + } + if (success) + { + // get length of stream: + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + + LL_DEBUGS(LOG_TXT) << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; + if (data_size > 0) + { + // *TODO: set the formatted image data here directly to avoid the copy + + // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); + body->addRef(); + mHttpBufferArray = body; + + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = data_size; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS(LOG_TXT) << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize) + { + mHaveAllData = TRUE; + } + else if (data_size > mRequestedSize) + { + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) + LL_WARNS(LOG_TXT) << "data_size = " << data_size << " > requested: " << mRequestedSize << LL_ENDL; + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + } + else + { + // We requested data but received none (and no error), + // so presumably we have all of it + mHaveAllData = TRUE; + } + mRequestedSize = data_size; + + if (mHaveAllData) { LLViewerStatsRecorder::instance().textureFetch(); } // *TODO: set the formatted image data here directly to avoid the copy - } - else - { - mRequestedSize = -1; // error - } - - mLoaded = TRUE; + } + else + { + mRequestedSize = -1; // error + } + + mLoaded = TRUE; - return data_size ; + return data_size ; } ////////////////////////////////////////////////////////////////////////////// // Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, - S32 imagesize, BOOL islocal) + S32 imagesize, BOOL islocal) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - LLMutexLock lock(&mWorkMutex); // +Mw - if (mState != LOAD_FROM_TEXTURE_CACHE) - { -// LL_WARNS(LOG_TXT) << "Read callback for " << mID << " with state = " << mState << LL_ENDL; - return; - } - if (success) - { - llassert_always(imagesize >= 0); - mFileSize = imagesize; - mFormattedImage = image; - mImageCodec = image->getCodec(); - mInLocalCache = islocal; - if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize) - { - mHaveAllData = TRUE; - } - } - mLoaded = TRUE; -} // -Mw + LLMutexLock lock(&mWorkMutex); // +Mw + if (mState != LOAD_FROM_TEXTURE_CACHE) + { +// LL_WARNS(LOG_TXT) << "Read callback for " << mID << " with state = " << mState << LL_ENDL; + return; + } + if (success) + { + llassert_always(imagesize >= 0); + mFileSize = imagesize; + mFormattedImage = image; + mImageCodec = image->getCodec(); + mInLocalCache = islocal; + if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize) + { + mHaveAllData = TRUE; + } + } + mLoaded = TRUE; +} // -Mw // Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); // +Mw - if (mState != WAIT_ON_WRITE) - { -// LL_WARNS(LOG_TXT) << "Write callback for " << mID << " with state = " << mState << LL_ENDL; - return; - } - mWritten = TRUE; -} // -Mw + LLMutexLock lock(&mWorkMutex); // +Mw + if (mState != WAIT_ON_WRITE) + { +// LL_WARNS(LOG_TXT) << "Write callback for " << mID << " with state = " << mState << LL_ENDL; + return; + } + mWritten = TRUE; +} // -Mw ////////////////////////////////////////////////////////////////////////////// // Threads: Tid -void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux, S32 decode_id) +void LLTextureFetchWorker::callbackDecoded(bool success, const std::string &error_message, LLImageRaw* raw, LLImageRaw* aux, S32 decode_id) { - LLMutexLock lock(&mWorkMutex); // +Mw - if (mDecodeHandle == 0) - { - return; // aborted, ignore - } + LLMutexLock lock(&mWorkMutex); // +Mw + if (mDecodeHandle == 0) + { + return; // aborted, ignore + } if (mDecodeHandle != decode_id) { // Queue doesn't support canceling old requests. @@ -2334,87 +2334,87 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag LL_DEBUGS(LOG_TXT) << mID << " received obsolete decode's callback" << LL_ENDL; return; // ignore } - if (mState != DECODE_IMAGE_UPDATE) - { - LL_DEBUGS(LOG_TXT) << "Decode callback for " << mID << " with state = " << mState << LL_ENDL; - mDecodeHandle = 0; - return; - } - llassert_always(mFormattedImage.notNull()); - - mDecodeHandle = 0; - if (success) - { - llassert_always(raw); - mRawImage = raw; - mAuxImage = aux; - mDecodedDiscard = mFormattedImage->getDiscardLevel(); - LL_DEBUGS(LOG_TXT) << mID << ": Decode Finished. Discard: " << mDecodedDiscard - << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; - } - else - { - LL_WARNS(LOG_TXT) << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << LL_ENDL; - removeFromCache(); - mDecodedDiscard = -1; // Redundant, here for clarity and paranoia - } - mDecoded = TRUE; -// LL_INFOS(LOG_TXT) << mID << " : DECODE COMPLETE " << LL_ENDL; -} // -Mw + if (mState != DECODE_IMAGE_UPDATE) + { + LL_DEBUGS(LOG_TXT) << "Decode callback for " << mID << " with state = " << mState << LL_ENDL; + mDecodeHandle = 0; + return; + } + llassert_always(mFormattedImage.notNull()); + + mDecodeHandle = 0; + if (success) + { + llassert_always(raw); + mRawImage = raw; + mAuxImage = aux; + mDecodedDiscard = mFormattedImage->getDiscardLevel(); + LL_DEBUGS(LOG_TXT) << mID << ": Decode Finished. Discard: " << mDecodedDiscard + << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; + } + else + { + LL_WARNS(LOG_TXT) << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << ", reason: " << error_message << LL_ENDL; + removeFromCache(); + mDecodedDiscard = -1; // Redundant, here for clarity and paranoia + } + mDecoded = TRUE; +// LL_INFOS(LOG_TXT) << mID << " : DECODE COMPLETE " << LL_ENDL; +} // -Mw ////////////////////////////////////////////////////////////////////////////// // Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { - // Complete write to cache - if (mCacheWriteHandle != LLTextureCache::nullHandle()) - { - if (!mWritten) - { - return false; - } - if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) - { - mCacheWriteHandle = LLTextureCache::nullHandle(); - } - else - { - return false; - } - } - return true; + // Complete write to cache + if (mCacheWriteHandle != LLTextureCache::nullHandle()) + { + if (!mWritten) + { + return false; + } + if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) + { + mCacheWriteHandle = LLTextureCache::nullHandle(); + } + else + { + return false; + } + } + return true; } // Threads: Ttf void LLTextureFetchWorker::recordTextureStart(bool is_http) { - if (! mMetricsStartTime.value()) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, - is_http, - LLImageBase::TYPE_AVATAR_BAKE == mType); + if (! mMetricsStartTime.value()) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); } // Threads: Ttf void LLTextureFetchWorker::recordTextureDone(bool is_http, F64 byte_count) { - if (mMetricsStartTime.value()) - { - LLViewerAssetStatsFF::record_response(LLViewerAssetType::AT_TEXTURE, + if (mMetricsStartTime.value()) + { + LLViewerAssetStatsFF::record_response(LLViewerAssetType::AT_TEXTURE, is_http, LLImageBase::TYPE_AVATAR_BAKE == mType, LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime, byte_count); - mMetricsStartTime = (U32Seconds)0; - } - LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_TEXTURE, - is_http, - LLImageBase::TYPE_AVATAR_BAKE == mType); + mMetricsStartTime = (U32Seconds)0; + } + LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); } @@ -2433,201 +2433,201 @@ std::string LLTextureFetch::getStateString(S32 state) } LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded, bool qa_mode) - : LLWorkerThread("TextureFetch", threaded, true), - mDebugCount(0), - mDebugPause(FALSE), - mPacketCount(0), - mBadPacketCount(0), - mQueueMutex(), - mNetworkQueueMutex(), - mTextureCache(cache), - mTextureBandwidth(0), - mHTTPTextureBits(0), - mTotalHTTPRequests(0), - mQAMode(qa_mode), - mHttpRequest(NULL), - mHttpOptions(), - mHttpOptionsWithHeaders(), - mHttpHeaders(), - mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpMetricsHeaders(), - mHttpMetricsPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mTotalCacheReadCount(0U), - mTotalCacheWriteCount(0U), - mTotalResourceWaitCount(0U), - mFetchSource(LLTextureFetch::FROM_ALL), - mOriginFetchSource(LLTextureFetch::FROM_ALL), - mTextureInfoMainThread(false) -{ - mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); - mTextureInfo.setLogging(true); - - LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - mHttpRequest = new LLCore::HttpRequest; - mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - mHttpOptionsWithHeaders = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - mHttpOptionsWithHeaders->setWantHeaders(true); + : LLWorkerThread("TextureFetch", threaded, true), + mDebugCount(0), + mDebugPause(FALSE), + mPacketCount(0), + mBadPacketCount(0), + mQueueMutex(), + mNetworkQueueMutex(), + mTextureCache(cache), + mTextureBandwidth(0), + mHTTPTextureBits(0), + mTotalHTTPRequests(0), + mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(), + mHttpOptionsWithHeaders(), + mHttpHeaders(), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpMetricsHeaders(), + mHttpMetricsPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U), + mFetchSource(LLTextureFetch::FROM_ALL), + mOriginFetchSource(LLTextureFetch::FROM_ALL), + mTextureInfoMainThread(false) +{ + mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + mTextureInfo.setLogging(true); + + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpOptionsWithHeaders = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + mHttpOptionsWithHeaders->setWantHeaders(true); mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); - mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); - mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_IMAGE_X_J2C); + mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_TEXTURE); mHttpMetricsHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); - mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); - mHttpMetricsPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_REPORTING); - mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; - mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; - mHttpSemaphore = 0; - - // If that test log has ben requested but not yet created, create it - if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) - { - sTesterp = new LLTextureFetchTester() ; - if (!sTesterp->isValid()) - { - delete sTesterp; - sTesterp = NULL; - } - } + mHttpMetricsHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + mHttpMetricsPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_REPORTING); + mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; + mHttpSemaphore = 0; + + // If that test log has ben requested but not yet created, create it + if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName)) + { + sTesterp = new LLTextureFetchTester() ; + if (!sTesterp->isValid()) + { + delete sTesterp; + sTesterp = NULL; + } + } } LLTextureFetch::~LLTextureFetch() { - clearDeleteList(); + clearDeleteList(); - while (! mCommands.empty()) - { - TFRequest * req(mCommands.front()); - mCommands.erase(mCommands.begin()); - delete req; - } + while (! mCommands.empty()) + { + TFRequest * req(mCommands.front()); + mCommands.erase(mCommands.begin()); + delete req; + } - mHttpWaitResource.clear(); - - delete mHttpRequest; - mHttpRequest = NULL; + mHttpWaitResource.clear(); - // ~LLQueuedThread() called here + delete mHttpRequest; + mHttpRequest = NULL; + + // ~LLQueuedThread() called here } S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, - S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http) + S32 w, S32 h, S32 c, S32 desired_discard, bool needs_aux, bool can_use_http) { LL_PROFILE_ZONE_SCOPED; - if (mDebugPause) - { - return -1; - } - - if (f_type == FTT_SERVER_BAKE) - { - LL_DEBUGS("Avatar") << " requesting " << id << " " << w << "x" << h << " discard " << desired_discard << " type " << f_type << LL_ENDL; - } - LLTextureFetchWorker* worker = getWorker(id) ; - if (worker) - { - if (worker->mHost != host) - { - LL_WARNS(LOG_TXT) << "LLTextureFetch::createRequest " << id << " called with multiple hosts: " - << host << " != " << worker->mHost << LL_ENDL; - removeRequest(worker, true); - worker = NULL; - return -1; - } - } - - S32 desired_size; - std::string exten = gDirUtilp->getExtension(url); - //if (f_type == FTT_SERVER_BAKE) + if (mDebugPause) + { + return -1; + } + + if (f_type == FTT_SERVER_BAKE) + { + LL_DEBUGS("Avatar") << " requesting " << id << " " << w << "x" << h << " discard " << desired_discard << " type " << f_type << LL_ENDL; + } + LLTextureFetchWorker* worker = getWorker(id) ; + if (worker) + { + if (worker->mHost != host) + { + LL_WARNS(LOG_TXT) << "LLTextureFetch::createRequest " << id << " called with multiple hosts: " + << host << " != " << worker->mHost << LL_ENDL; + removeRequest(worker, true); + worker = NULL; + return -1; + } + } + + S32 desired_size; + std::string exten = gDirUtilp->getExtension(url); + //if (f_type == FTT_SERVER_BAKE) if ((f_type == FTT_SERVER_BAKE) && !url.empty() && !exten.empty() && (LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) - { - // SH-4030: This case should be redundant with the following one, just - // breaking it out here to clarify that it's intended behavior. - llassert(!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)); - - // Do full requests for baked textures to reduce interim blurring. - LL_DEBUGS(LOG_TXT) << "full request for " << id << " texture is FTT_SERVER_BAKE" << LL_ENDL; - desired_size = MAX_IMAGE_DATA_SIZE; - desired_discard = 0; - } - else if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) - { - LL_DEBUGS(LOG_TXT) << "full request for " << id << " exten is not J2C: " << exten << LL_ENDL; - // Only do partial requests for J2C at the moment - desired_size = MAX_IMAGE_DATA_SIZE; - desired_discard = 0; - } - else if (desired_discard == 0) - { - // if we want the entire image, and we know its size, then get it all - // (calcDataSizeJ2C() below makes assumptions about how the image - // was compressed - this code ensures that when we request the entire image, - // we really do get it.) - desired_size = MAX_IMAGE_DATA_SIZE; - } - else if (w*h*c > 0) - { - // If the requester knows the dimensions of the image, - // this will calculate how much data we need without having to parse the header - - desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard); - } - else - { - // If the requester knows nothing about the file, we fetch the smallest - // amount of data at the lowest resolution (highest discard level) possible. - desired_size = TEXTURE_CACHE_ENTRY_SIZE; - desired_discard = MAX_DISCARD_LEVEL; - } - - - if (worker) - { - if (worker->wasAborted()) - { - return -1; // need to wait for previous aborted request to complete - } - worker->lockWorkMutex(); // +Mw + { + // SH-4030: This case should be redundant with the following one, just + // breaking it out here to clarify that it's intended behavior. + llassert(!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)); + + // Do full requests for baked textures to reduce interim blurring. + LL_DEBUGS(LOG_TXT) << "full request for " << id << " texture is FTT_SERVER_BAKE" << LL_ENDL; + desired_size = MAX_IMAGE_DATA_SIZE; + desired_discard = 0; + } + else if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) + { + LL_DEBUGS(LOG_TXT) << "full request for " << id << " exten is not J2C: " << exten << LL_ENDL; + // Only do partial requests for J2C at the moment + desired_size = MAX_IMAGE_DATA_SIZE; + desired_discard = 0; + } + else if (desired_discard == 0) + { + // if we want the entire image, and we know its size, then get it all + // (calcDataSizeJ2C() below makes assumptions about how the image + // was compressed - this code ensures that when we request the entire image, + // we really do get it.) + desired_size = MAX_IMAGE_DATA_SIZE; + } + else if (w*h*c > 0) + { + // If the requester knows the dimensions of the image, + // this will calculate how much data we need without having to parse the header + + desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, desired_discard); + } + else + { + // If the requester knows nothing about the file, we fetch the smallest + // amount of data at the lowest resolution (highest discard level) possible. + desired_size = TEXTURE_CACHE_ENTRY_SIZE; + desired_discard = MAX_DISCARD_LEVEL; + } + + + if (worker) + { + if (worker->wasAborted()) + { + return -1; // 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 + worker->unlockWorkMutex(); // -Mw return -1; // similar request has failed or is in a transitional state } - worker->mActiveCount++; - worker->mNeedsAux = needs_aux; - worker->setImagePriority(priority); - worker->setDesiredDiscard(desired_discard, desired_size); - worker->setCanUseHTTP(can_use_http); - - //MAINT-4184 url is always empty. Do not set with it. - - if (!worker->haveWork()) - { - worker->setState(LLTextureFetchWorker::INIT); - worker->unlockWorkMutex(); // -Mw - - worker->addWork(0); - } - else - { - worker->unlockWorkMutex(); // -Mw - } - } - else - { - worker = new LLTextureFetchWorker(this, f_type, url, id, host, priority, desired_discard, desired_size); - lockQueue(); // +Mfq - mRequestMap[id] = worker; - unlockQueue(); // -Mfq - - worker->lockWorkMutex(); // +Mw - worker->mActiveCount++; - worker->mNeedsAux = needs_aux; - worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); // -Mw - } - - LL_DEBUGS(LOG_TXT) << "REQUESTED: " << id << " f_type " << fttype_to_string(f_type) - << " Discard: " << desired_discard << " size " << desired_size << LL_ENDL; - return desired_discard; + worker->mActiveCount++; + worker->mNeedsAux = needs_aux; + worker->setImagePriority(priority); + worker->setDesiredDiscard(desired_discard, desired_size); + worker->setCanUseHTTP(can_use_http); + + //MAINT-4184 url is always empty. Do not set with it. + + if (!worker->haveWork()) + { + worker->setState(LLTextureFetchWorker::INIT); + worker->unlockWorkMutex(); // -Mw + + worker->addWork(0); + } + else + { + worker->unlockWorkMutex(); // -Mw + } + } + else + { + worker = new LLTextureFetchWorker(this, f_type, url, id, host, priority, desired_discard, desired_size); + lockQueue(); // +Mfq + mRequestMap[id] = worker; + unlockQueue(); // -Mfq + + worker->lockWorkMutex(); // +Mw + worker->mActiveCount++; + worker->mNeedsAux = needs_aux; + worker->setCanUseHTTP(can_use_http) ; + worker->unlockWorkMutex(); // -Mw + } + + LL_DEBUGS(LOG_TXT) << "REQUESTED: " << id << " f_type " << fttype_to_string(f_type) + << " Discard: " << desired_discard << " size " << desired_size << LL_ENDL; + return desired_discard; } // Threads: T* // @@ -2635,19 +2635,19 @@ S32 LLTextureFetch::createRequest(FTType f_type, const std::string& url, const L void LLTextureFetch::addToHTTPQueue(const LLUUID& id) { LL_PROFILE_ZONE_SCOPED; - LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq - mHTTPTextureQueue.insert(id); - mTotalHTTPRequests++; -} // -Mfnq + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.insert(id); + mTotalHTTPRequests++; +} // -Mfnq // Threads: T* void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32Bytes received_size) { LL_PROFILE_ZONE_SCOPED; - LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq - mHTTPTextureQueue.erase(id); - mHTTPTextureBits += received_size; // Approximate - does not include header bits -} // -Mfnq + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.erase(id); + mHTTPTextureBits += received_size; // Approximate - does not include header bits +} // -Mfnq // NB: If you change deleteRequest() you should probably make // parallel changes in removeRequest(). They're functionally @@ -2657,22 +2657,22 @@ void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32Bytes received_siz void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { LL_PROFILE_ZONE_SCOPED; - lockQueue(); // +Mfq - LLTextureFetchWorker* worker = getWorkerAfterLock(id); - if (worker) - { - size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue(); // -Mfq + lockQueue(); // +Mfq + LLTextureFetchWorker* worker = getWorkerAfterLock(id); + if (worker) + { + size_t erased_1 = mRequestMap.erase(worker->mID); + unlockQueue(); // -Mfq - llassert_always(erased_1 > 0) ; - llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; + llassert_always(erased_1 > 0) ; + llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - worker->scheduleDelete(); - } - else - { - unlockQueue(); // -Mfq - } + worker->scheduleDelete(); + } + else + { + unlockQueue(); // -Mfq + } } // NB: If you change removeRequest() you should probably make @@ -2683,67 +2683,67 @@ void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { LL_PROFILE_ZONE_SCOPED; - if(!worker) - { - return; - } + if(!worker) + { + return; + } - lockQueue(); // +Mfq - size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue(); // -Mfq + lockQueue(); // +Mfq + size_t erased_1 = mRequestMap.erase(worker->mID); + unlockQueue(); // -Mfq - llassert_always(erased_1 > 0) ; - llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; + llassert_always(erased_1 > 0) ; + llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - worker->scheduleDelete(); + worker->scheduleDelete(); } void LLTextureFetch::deleteAllRequests() { - while(1) - { - lockQueue(); - if(mRequestMap.empty()) - { - unlockQueue() ; - break; - } + while(1) + { + lockQueue(); + if(mRequestMap.empty()) + { + unlockQueue() ; + break; + } - LLTextureFetchWorker* worker = mRequestMap.begin()->second; - unlockQueue() ; + LLTextureFetchWorker* worker = mRequestMap.begin()->second; + unlockQueue() ; - removeRequest(worker, true); - } + removeRequest(worker, true); + } } // Threads: T* -S32 LLTextureFetch::getNumRequests() -{ - lockQueue(); // +Mfq - S32 size = (S32)mRequestMap.size(); - unlockQueue(); // -Mfq +S32 LLTextureFetch::getNumRequests() +{ + lockQueue(); // +Mfq + S32 size = (S32)mRequestMap.size(); + unlockQueue(); // -Mfq - return size; + return size; } // Threads: T* -S32 LLTextureFetch::getNumHTTPRequests() -{ - mNetworkQueueMutex.lock(); // +Mfq - S32 size = (S32)mHTTPTextureQueue.size(); - mNetworkQueueMutex.unlock(); // -Mfq +S32 LLTextureFetch::getNumHTTPRequests() +{ + mNetworkQueueMutex.lock(); // +Mfq + S32 size = (S32)mHTTPTextureQueue.size(); + mNetworkQueueMutex.unlock(); // -Mfq - return size; + return size; } // Threads: T* U32 LLTextureFetch::getTotalNumHTTPRequests() { - mNetworkQueueMutex.lock(); // +Mfq - U32 size = mTotalHTTPRequests; - mNetworkQueueMutex.unlock(); // -Mfq + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq - return size; + return size; } // call lockQueue() first! @@ -2752,115 +2752,115 @@ U32 LLTextureFetch::getTotalNumHTTPRequests() LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LL_PROFILE_ZONE_SCOPED; - LLTextureFetchWorker* res = NULL; - map_t::iterator iter = mRequestMap.find(id); - if (iter != mRequestMap.end()) - { - res = iter->second; - } - return res; + LLTextureFetchWorker* res = NULL; + map_t::iterator iter = mRequestMap.find(id); + if (iter != mRequestMap.end()) + { + res = iter->second; + } + return res; } // Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex); // +Mfq + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id); -} // -Mfq + return getWorkerAfterLock(id); +} // -Mfq // Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, - LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux, - LLCore::HttpStatus& last_http_get_status) + LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux, + LLCore::HttpStatus& last_http_get_status) { LL_PROFILE_ZONE_SCOPED; - bool res = false; - LLTextureFetchWorker* worker = getWorker(id); - if (worker) - { - if (worker->wasAborted()) - { - res = true; - } - else if (!worker->haveWork()) - { - // Should only happen if we set mDebugPause... - if (!mDebugPause) - { -// LL_WARNS(LOG_TXT) << "Adding work for inactive worker: " << id << LL_ENDL; - worker->addWork(0); - } - } - else if (worker->checkWork()) - { - F32 decode_time; - F32 fetch_time; - F32 cache_read_time; - F32 cache_write_time; - S32 file_size; - std::map<S32, F32> logged_state_timers; - F32 skipped_states_time; - worker->lockWorkMutex(); // +Mw - last_http_get_status = worker->mGetStatus; - discard_level = worker->mDecodedDiscard; - raw = worker->mRawImage; - aux = worker->mAuxImage; - - decode_time = worker->mDecodeTime; - fetch_time = worker->mFetchTime; - cache_read_time = worker->mCacheReadTime; - cache_write_time = worker->mCacheWriteTime; - file_size = worker->mFileSize; + bool res = false; + LLTextureFetchWorker* worker = getWorker(id); + if (worker) + { + if (worker->wasAborted()) + { + res = true; + } + else if (!worker->haveWork()) + { + // Should only happen if we set mDebugPause... + if (!mDebugPause) + { +// LL_WARNS(LOG_TXT) << "Adding work for inactive worker: " << id << LL_ENDL; + worker->addWork(0); + } + } + else if (worker->checkWork()) + { + F32 decode_time; + F32 fetch_time; + F32 cache_read_time; + F32 cache_write_time; + S32 file_size; + std::map<S32, F32> logged_state_timers; + F32 skipped_states_time; + worker->lockWorkMutex(); // +Mw + last_http_get_status = worker->mGetStatus; + discard_level = worker->mDecodedDiscard; + raw = worker->mRawImage; + aux = worker->mAuxImage; + + decode_time = worker->mDecodeTime; + fetch_time = worker->mFetchTime; + cache_read_time = worker->mCacheReadTime; + cache_write_time = worker->mCacheWriteTime; + file_size = worker->mFileSize; worker->mCacheReadTimer.reset(); worker->mDecodeTimer.reset(); - worker->mCacheWriteTimer.reset(); + worker->mCacheWriteTimer.reset(); worker->mFetchTimer.reset(); - logged_state_timers = worker->mStateTimersMap; - skipped_states_time = worker->mSkippedStatesTime; - worker->mStateTimer.reset(); - res = true; - LL_DEBUGS(LOG_TXT) << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); // -Mw - - sample(sTexDecodeLatency, decode_time); - sample(sTexFetchLatency, fetch_time); - sample(sCacheReadLatency, cache_read_time); - sample(sCacheWriteLatency, cache_write_time); - - static LLCachedControl<F32> min_time_to_log(gSavedSettings, "TextureFetchMinTimeToLog", 2.f); - if (fetch_time > min_time_to_log) - { - //LL_INFOS() << "fetch_time: " << fetch_time << " cache_read_time: " << cache_read_time << " decode_time: " << decode_time << " cache_write_time: " << cache_write_time << LL_ENDL; - - LLTextureFetchTester* tester = (LLTextureFetchTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); - if (tester) - { - tester->updateStats(logged_state_timers, fetch_time, skipped_states_time, file_size) ; - } - } - } - else - { - worker->lockWorkMutex(); // +Mw - if ((worker->mDecodedDiscard >= 0) && - (worker->mDecodedDiscard < discard_level || discard_level < 0) && - (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) - { - // Not finished, but data is ready - discard_level = worker->mDecodedDiscard; - raw = worker->mRawImage; - aux = worker->mAuxImage; - } - worker->unlockWorkMutex(); // -Mw - } - } - else - { - res = true; - } - return res; + logged_state_timers = worker->mStateTimersMap; + skipped_states_time = worker->mSkippedStatesTime; + worker->mStateTimer.reset(); + res = true; + LL_DEBUGS(LOG_TXT) << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; + worker->unlockWorkMutex(); // -Mw + + sample(sTexDecodeLatency, decode_time); + sample(sTexFetchLatency, fetch_time); + sample(sCacheReadLatency, cache_read_time); + sample(sCacheWriteLatency, cache_write_time); + + static LLCachedControl<F32> min_time_to_log(gSavedSettings, "TextureFetchMinTimeToLog", 2.f); + if (fetch_time > min_time_to_log) + { + //LL_INFOS() << "fetch_time: " << fetch_time << " cache_read_time: " << cache_read_time << " decode_time: " << decode_time << " cache_write_time: " << cache_write_time << LL_ENDL; + + LLTextureFetchTester* tester = (LLTextureFetchTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName); + if (tester) + { + tester->updateStats(logged_state_timers, fetch_time, skipped_states_time, file_size) ; + } + } + } + else + { + worker->lockWorkMutex(); // +Mw + if ((worker->mDecodedDiscard >= 0) && + (worker->mDecodedDiscard < discard_level || discard_level < 0) && + (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) + { + // Not finished, but data is ready + discard_level = worker->mDecodedDiscard; + raw = worker->mRawImage; + aux = worker->mAuxImage; + } + worker->unlockWorkMutex(); // -Mw + } + } + else + { + res = true; + } + return res; } // Threads: T* @@ -2872,13 +2872,13 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); // +Mw + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); // -Mw + worker->unlockWorkMutex(); // -Mw } }); - - return true; + + return true; } // Replicates and expands upon the base class's @@ -2897,14 +2897,14 @@ size_t LLTextureFetch::getPending() { LL_PROFILE_ZONE_SCOPED; size_t res; - lockData(); // +Ct + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); // +Mfq - + LLMutexLock lock(&mQueueMutex); // +Mfq + res = mRequestQueue.size(); res += mCommands.size(); - } // -Mfq - unlockData(); // -Ct + } // -Mfq + unlockData(); // -Ct return res; } @@ -2912,24 +2912,24 @@ size_t LLTextureFetch::getPending() // virtual bool LLTextureFetch::runCondition() { - // Caller is holding the lock on LLThread's condition variable. - - // LLQueuedThread, unlike its base class LLThread, makes this a - // private method which is unfortunate. I want to use it directly - // but I'm going to have to re-implement the logic here (or change - // declarations, which I don't want to do right now). - // - // Changes here may need to be reflected in getPending(). - - bool have_no_commands(false); - { - LLMutexLock lock(&mQueueMutex); // +Mfq - - have_no_commands = mCommands.empty(); - } // -Mfq - - return ! (have_no_commands - && (mRequestQueue.size() == 0 && mIdleThread)); // From base class + // Caller is holding the lock on LLThread's condition variable. + + // LLQueuedThread, unlike its base class LLThread, makes this a + // private method which is unfortunate. I want to use it directly + // but I'm going to have to re-implement the logic here (or change + // declarations, which I don't want to do right now). + // + // Changes here may need to be reflected in getPending(). + + bool have_no_commands(false); + { + LLMutexLock lock(&mQueueMutex); // +Mfq + + have_no_commands = mCommands.empty(); + } // -Mfq + + return ! (have_no_commands + && (mRequestQueue.size() == 0 && mIdleThread)); // From base class } ////////////////////////////////////////////////////////////////////////////// @@ -2938,34 +2938,34 @@ bool LLTextureFetch::runCondition() void LLTextureFetch::commonUpdate() { LL_PROFILE_ZONE_SCOPED; - // Update low/high water levels based on pipelining. We pick - // up setting eventually, so the semaphore/request level can - // fall outside the [0..HIGH_WATER] range. Expect that. - if (LLAppViewer::instance()->getAppCoreHttp().isPipelined(LLAppCoreHttp::AP_TEXTURE)) - { - mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; - mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; - } - else - { - mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; - mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; - } - - // Release waiters - releaseHttpWaiters(); - - // Run a cross-thread command, if any. - cmdDoWork(); - - // Deliver all completion notifications - LLCore::HttpStatus status = mHttpRequest->update(0); - if (! status) - { - LL_INFOS_ONCE(LOG_TXT) << "Problem during HTTP servicing. Reason: " - << status.toString() - << LL_ENDL; - } + // Update low/high water levels based on pipelining. We pick + // up setting eventually, so the semaphore/request level can + // fall outside the [0..HIGH_WATER] range. Expect that. + if (LLAppViewer::instance()->getAppCoreHttp().isPipelined(LLAppCoreHttp::AP_TEXTURE)) + { + mHttpHighWater = HTTP_PIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_PIPE_REQUESTS_LOW_WATER; + } + else + { + mHttpHighWater = HTTP_NONPIPE_REQUESTS_HIGH_WATER; + mHttpLowWater = HTTP_NONPIPE_REQUESTS_LOW_WATER; + } + + // Release waiters + releaseHttpWaiters(); + + // Run a cross-thread command, if any. + cmdDoWork(); + + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); + if (! status) + { + LL_INFOS_ONCE(LOG_TXT) << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; + } } @@ -2975,89 +2975,89 @@ void LLTextureFetch::commonUpdate() size_t LLTextureFetch::update(F32 max_time_ms) { LL_PROFILE_ZONE_SCOPED; - static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS", 3000.0); + static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS", 3000.0); - { - mNetworkQueueMutex.lock(); // +Mfnq - mMaxBandwidth = band_width(); + { + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width(); - add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, mHTTPTextureBits); - mHTTPTextureBits = (U32Bits)0; + add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, mHTTPTextureBits); + mHTTPTextureBits = (U32Bits)0; - mNetworkQueueMutex.unlock(); // -Mfnq - } + mNetworkQueueMutex.unlock(); // -Mfnq + } - size_t res = LLWorkerThread::update(max_time_ms); - - if (!mThreaded) - { - commonUpdate(); - } + size_t res = LLWorkerThread::update(max_time_ms); - return res; + if (!mThreaded) + { + commonUpdate(); + } + + return res; } // called in the MAIN thread after the TextureCacheThread shuts down. // // Threads: Tmain -void LLTextureFetch::shutDownTextureCacheThread() +void LLTextureFetch::shutDownTextureCacheThread() { - if(mTextureCache) - { - llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ; - mTextureCache = NULL ; - } + if(mTextureCache) + { + llassert_always(mTextureCache->isQuitting() || mTextureCache->isStopped()) ; + mTextureCache = NULL ; + } } - + // Threads: Ttf void LLTextureFetch::startThread() { - mTextureInfo.startRecording(); + mTextureInfo.startRecording(); } // Threads: Ttf void LLTextureFetch::endThread() { - LL_INFOS(LOG_TXT) << "CacheReads: " << mTotalCacheReadCount - << ", CacheWrites: " << mTotalCacheWriteCount - << ", ResWaits: " << mTotalResourceWaitCount - << ", TotalHTTPReq: " << getTotalNumHTTPRequests() - << LL_ENDL; + LL_INFOS(LOG_TXT) << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; - mTextureInfo.stopRecording(); + mTextureInfo.stopRecording(); } // Threads: Ttf void LLTextureFetch::threadedUpdate() { LL_PROFILE_ZONE_SCOPED; - llassert_always(mHttpRequest); + llassert_always(mHttpRequest); #if 0 - // Limit update frequency - const F32 PROCESS_TIME = 0.05f; - static LLFrameTimer process_timer; - if (process_timer.getElapsedTimeF32() < PROCESS_TIME) - { - return; - } - process_timer.reset(); + // Limit update frequency + const F32 PROCESS_TIME = 0.05f; + static LLFrameTimer process_timer; + if (process_timer.getElapsedTimeF32() < PROCESS_TIME) + { + return; + } + process_timer.reset(); #endif - - commonUpdate(); - + + commonUpdate(); + #if 0 - const F32 INFO_TIME = 1.0f; - static LLFrameTimer info_timer; - if (info_timer.getElapsedTimeF32() >= INFO_TIME) - { - S32 q = mCurlGetRequest->getQueued(); - if (q > 0) - { - LL_INFOS(LOG_TXT) << "Queued gets: " << q << LL_ENDL; - info_timer.reset(); - } - } + const F32 INFO_TIME = 1.0f; + static LLFrameTimer info_timer; + if (info_timer.getElapsedTimeF32() >= INFO_TIME) + { + S32 q = mCurlGetRequest->getQueued(); + if (q > 0) + { + LL_INFOS(LOG_TXT) << "Queued gets: " << q << LL_ENDL; + info_timer.reset(); + } + } #endif } @@ -3068,63 +3068,63 @@ void LLTextureFetch::threadedUpdate() bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { LL_PROFILE_ZONE_SCOPED; - mRequestedDeltaTimer.reset(); - if (index >= mTotalPackets) - { -// LL_WARNS(LOG_TXT) << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << LL_ENDL; - return false; - } - if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE) - { -// LL_WARNS(LOG_TXT) << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << LL_ENDL; - return false; - } - - if (index >= (S32)mPackets.size()) - { - mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers - } - else if (mPackets[index] != NULL) - { -// LL_WARNS(LOG_TXT) << "Received duplicate packet: " << index << " for image: " << mID << LL_ENDL; - return false; - } - - mPackets[index] = new PacketData(data, size); - while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL) - { - ++mLastPacket; - } - return true; + mRequestedDeltaTimer.reset(); + if (index >= mTotalPackets) + { +// LL_WARNS(LOG_TXT) << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << LL_ENDL; + return false; + } + if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE) + { +// LL_WARNS(LOG_TXT) << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << LL_ENDL; + return false; + } + + if (index >= (S32)mPackets.size()) + { + mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers + } + else if (mPackets[index] != NULL) + { +// LL_WARNS(LOG_TXT) << "Received duplicate packet: " << index << " for image: " << mID << LL_ENDL; + return false; + } + + mPackets[index] = new PacketData(data, size); + while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL) + { + ++mLastPacket; + } + return true; } void LLTextureFetchWorker::setState(e_state new_state) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - if (mFTType == FTT_SERVER_BAKE) - { - // NOTE: turning on these log statements is a reliable way to get - // blurry images fairly frequently. Presumably this is an - // indication of some subtle timing or locking issue. - -// LL_INFOS(LOG_TXT) << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << LL_ENDL; - } - - F32 d_time = mStateTimer.getElapsedTimeF32(); - if (d_time >= 0.0001F) - { - if (LOGGED_STATES.count(mState)) - { - mStateTimersMap[mState] = d_time; - } - else - { - mSkippedStatesTime += d_time; - } - } - - mStateTimer.reset(); - mState = new_state; + if (mFTType == FTT_SERVER_BAKE) + { + // NOTE: turning on these log statements is a reliable way to get + // blurry images fairly frequently. Presumably this is an + // indication of some subtle timing or locking issue. + +// LL_INFOS(LOG_TXT) << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << LL_ENDL; + } + + F32 d_time = mStateTimer.getElapsedTimeF32(); + if (d_time >= 0.0001F) + { + if (LOGGED_STATES.count(mState)) + { + mStateTimersMap[mState] = d_time; + } + else + { + mSkippedStatesTime += d_time; + } + } + + mStateTimer.reset(); + mState = new_state; } LLViewerRegion* LLTextureFetchWorker::getRegion() @@ -3146,17 +3146,17 @@ LLViewerRegion* LLTextureFetchWorker::getRegion() // Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { - BOOL from_cache = FALSE ; + BOOL from_cache = FALSE ; - LLTextureFetchWorker* worker = getWorker(id); - if (worker) - { - worker->lockWorkMutex(); // +Mw - from_cache = worker->mInLocalCache; - worker->unlockWorkMutex(); // -Mw - } + LLTextureFetchWorker* worker = getWorker(id); + if (worker) + { + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw + } - return from_cache ; + return from_cache ; } S32 LLTextureFetch::getFetchState(const LLUUID& id) @@ -3173,67 +3173,67 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id) // Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, - U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) + U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { LL_PROFILE_ZONE_SCOPED; - S32 state = LLTextureFetchWorker::INVALID; - F32 data_progress = 0.0f; - F32 requested_priority = 0.0f; - F32 fetch_dtime = 999999.f; - F32 request_dtime = 999999.f; - U32 fetch_priority = 0; - - LLTextureFetchWorker* worker = getWorker(id); - if (worker && worker->haveWork()) - { - worker->lockWorkMutex(); // +Mw - state = worker->mState; - fetch_dtime = worker->mFetchDeltaTimer.getElapsedTimeF32(); - request_dtime = worker->mRequestedDeltaTimer.getElapsedTimeF32(); - if (worker->mFileSize > 0) - { - if (worker->mFormattedImage.notNull()) - { - data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; - } - } - if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ) - { - requested_priority = worker->mRequestedPriority; - } - else - { - requested_priority = worker->mImagePriority; - } - fetch_priority = worker->getImagePriority(); - can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); // -Mw - } - data_progress_p = data_progress; - requested_priority_p = requested_priority; - fetch_priority_p = fetch_priority; - fetch_dtime_p = fetch_dtime; - request_dtime_p = request_dtime; - return state; + S32 state = LLTextureFetchWorker::INVALID; + F32 data_progress = 0.0f; + F32 requested_priority = 0.0f; + F32 fetch_dtime = 999999.f; + F32 request_dtime = 999999.f; + U32 fetch_priority = 0; + + LLTextureFetchWorker* worker = getWorker(id); + if (worker && worker->haveWork()) + { + worker->lockWorkMutex(); // +Mw + state = worker->mState; + fetch_dtime = worker->mFetchDeltaTimer.getElapsedTimeF32(); + request_dtime = worker->mRequestedDeltaTimer.getElapsedTimeF32(); + if (worker->mFileSize > 0) + { + if (worker->mFormattedImage.notNull()) + { + data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; + } + } + if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::WAIT_HTTP_REQ) + { + requested_priority = worker->mRequestedPriority; + } + else + { + requested_priority = worker->mImagePriority; + } + fetch_priority = worker->getImagePriority(); + can_use_http = worker->getCanUseHTTP() ; + worker->unlockWorkMutex(); // -Mw + } + data_progress_p = data_progress; + requested_priority_p = requested_priority; + fetch_priority_p = fetch_priority; + fetch_dtime_p = fetch_dtime; + request_dtime_p = request_dtime; + return state; } void LLTextureFetch::dump() { - LL_INFOS(LOG_TXT) << "LLTextureFetch ACTIVE_HTTP:" << LL_ENDL; - for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); - mHTTPTextureQueue.end() != iter; - ++iter) - { - LL_INFOS(LOG_TXT) << " ID: " << (*iter) << LL_ENDL; - } + LL_INFOS(LOG_TXT) << "LLTextureFetch ACTIVE_HTTP:" << LL_ENDL; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + LL_INFOS(LOG_TXT) << " ID: " << (*iter) << LL_ENDL; + } - LL_INFOS(LOG_TXT) << "LLTextureFetch WAIT_HTTP_RESOURCE:" << LL_ENDL; - for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); - mHttpWaitResource.end() != iter; - ++iter) - { - LL_INFOS(LOG_TXT) << " ID: " << (*iter) << LL_ENDL; - } + LL_INFOS(LOG_TXT) << "LLTextureFetch WAIT_HTTP_RESOURCE:" << LL_ENDL; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + LL_INFOS(LOG_TXT) << " ID: " << (*iter) << LL_ENDL; + } } ////////////////////////////////////////////////////////////////////////////// @@ -3243,31 +3243,31 @@ void LLTextureFetch::dump() // Threads: Ttf void LLTextureFetch::addHttpWaiter(const LLUUID & tid) { - mNetworkQueueMutex.lock(); // +Mfnq - mHttpWaitResource.insert(tid); - mNetworkQueueMutex.unlock(); // -Mfnq + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq } // Threads: Ttf void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) { - mNetworkQueueMutex.lock(); // +Mfnq - wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); - if (mHttpWaitResource.end() != iter) - { - mHttpWaitResource.erase(iter); - } - mNetworkQueueMutex.unlock(); // -Mfnq + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq } // Threads: T* bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) { - mNetworkQueueMutex.lock(); // +Mfnq - wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); - const bool ret(mHttpWaitResource.end() != iter); - mNetworkQueueMutex.unlock(); // -Mfnq - return ret; + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + const bool ret(mHttpWaitResource.end() != iter); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; } // Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 @@ -3286,152 +3286,152 @@ bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) void LLTextureFetch::releaseHttpWaiters() { LL_PROFILE_ZONE_SCOPED; - // Use mHttpSemaphore rather than mHTTPTextureQueue.size() - // to avoid a lock. - if (mHttpSemaphore >= mHttpLowWater) - return; - S32 needed(mHttpHighWater - mHttpSemaphore); - if (needed <= 0) - { - // Would only happen if High/LowWater were changed behind - // our back. In that case, defer fill until usage falls within - // limits. - return; - } - - // Quickly make a copy of all the LLUIDs. Get off the - // mutex as early as possible. - typedef std::vector<LLUUID> uuid_vec_t; - uuid_vec_t tids; - - { - LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq - - if (mHttpWaitResource.empty()) - return; - tids.reserve(mHttpWaitResource.size()); - tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); - } // -Mfnq - - // Now lookup the UUUIDs to find valid requests and sort - // them in priority order, highest to lowest. We're going - // to modify priority later as a side-effect of releasing - // these objects. That, in turn, would violate the partial - // ordering assumption of std::set, std::map, etc. so we - // don't use those containers. We use a vector and an explicit - // sort to keep the containers valid later. - typedef std::vector<LLTextureFetchWorker *> worker_list_t; - worker_list_t tids2; - - tids2.reserve(tids.size()); - for (uuid_vec_t::iterator iter(tids.begin()); - tids.end() != iter; - ++iter) - { - LLTextureFetchWorker * worker(getWorker(* iter)); - if (worker) - { - tids2.push_back(worker); - } - else - { - // If worker isn't found, this should be due to a request - // for deletion. We signal our recognition that this - // uuid shouldn't be used for resource waiting anymore by - // erasing it from the resource waiter list. That allows - // deleteOK to do final deletion on the worker. - removeHttpWaiter(* iter); - } - } - tids.clear(); - - // Sort into priority order, if necessary and only as much as needed - if (tids2.size() > needed) - { - LLTextureFetchWorker::Compare compare; - std::partial_sort(tids2.begin(), tids2.begin() + needed, tids2.end(), compare); - } - - // Release workers up to the high water mark. Since we aren't - // holding any locks at this point, we can be in competition - // with other callers. Do defensive things like getting - // refreshed counts of requests and checking if someone else - // has moved any worker state around.... - for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) - { - LLTextureFetchWorker * worker(* iter2); - - worker->lockWorkMutex(); // +Mw - if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) - { - // Not in expected state, remove it, try the next one - worker->unlockWorkMutex(); // -Mw - LL_WARNS(LOG_TXT) << "Resource-waited texture " << worker->mID - << " in unexpected state: " << worker->mState - << ". Removing from wait list." - << LL_ENDL; - removeHttpWaiter(worker->mID); - continue; - } - - if (! worker->acquireHttpSemaphore()) - { - // Out of active slots, quit - worker->unlockWorkMutex(); // -Mw - break; - } - - worker->setState(LLTextureFetchWorker::SEND_HTTP_REQ); - worker->unlockWorkMutex(); // -Mw - - removeHttpWaiter(worker->mID); - } + // Use mHttpSemaphore rather than mHTTPTextureQueue.size() + // to avoid a lock. + if (mHttpSemaphore >= mHttpLowWater) + return; + S32 needed(mHttpHighWater - mHttpSemaphore); + if (needed <= 0) + { + // Would only happen if High/LowWater were changed behind + // our back. In that case, defer fill until usage falls within + // limits. + return; + } + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + typedef std::vector<LLUUID> uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector<LLTextureFetchWorker *> worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.push_back(worker); + } + else + { + // If worker isn't found, this should be due to a request + // for deletion. We signal our recognition that this + // uuid shouldn't be used for resource waiting anymore by + // erasing it from the resource waiter list. That allows + // deleteOK to do final deletion on the worker. + removeHttpWaiter(* iter); + } + } + tids.clear(); + + // Sort into priority order, if necessary and only as much as needed + if (tids2.size() > needed) + { + LLTextureFetchWorker::Compare compare; + std::partial_sort(tids2.begin(), tids2.begin() + needed, tids2.end(), compare); + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) + { + // Not in expected state, remove it, try the next one + worker->unlockWorkMutex(); // -Mw + LL_WARNS(LOG_TXT) << "Resource-waited texture " << worker->mID + << " in unexpected state: " << worker->mState + << ". Removing from wait list." + << LL_ENDL; + removeHttpWaiter(worker->mID); + continue; + } + + if (! worker->acquireHttpSemaphore()) + { + // Out of active slots, quit + worker->unlockWorkMutex(); // -Mw + break; + } + + worker->setState(LLTextureFetchWorker::SEND_HTTP_REQ); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } } // Threads: T* void LLTextureFetch::cancelHttpWaiters() { - mNetworkQueueMutex.lock(); // +Mfnq - mHttpWaitResource.clear(); - mNetworkQueueMutex.unlock(); // -Mfnq + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq } // Threads: T* int LLTextureFetch::getHttpWaitersCount() { - mNetworkQueueMutex.lock(); // +Mfnq - int ret(mHttpWaitResource.size()); - mNetworkQueueMutex.unlock(); // -Mfnq - return ret; + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; } // Threads: T* void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) { - LLMutexLock lock(&mQueueMutex); // +Mfq + LLMutexLock lock(&mQueueMutex); // +Mfq - mTotalCacheReadCount += cache_read; - mTotalCacheWriteCount += cache_write; - mTotalResourceWaitCount += res_wait; -} // -Mfq + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq // Threads: T* void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) { - U32 ret1(0U), ret2(0U), ret3(0U); - - { - LLMutexLock lock(&mQueueMutex); // +Mfq - ret1 = mTotalCacheReadCount; - ret2 = mTotalCacheWriteCount; - ret3 = mTotalResourceWaitCount; - } // -Mfq - - *cache_read = ret1; - *cache_write = ret2; - *res_wait = ret3; + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; } ////////////////////////////////////////////////////////////////////////////// @@ -3441,77 +3441,77 @@ void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * re // Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { - TFReqSetRegion * req = new TFReqSetRegion(region_handle); + TFReqSetRegion * req = new TFReqSetRegion(region_handle); - cmdEnqueue(req); + cmdEnqueue(req); } // Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, - const LLUUID & session_id, - const LLUUID & agent_id, - LLSD& stats_sd) + const LLUUID & session_id, + const LLUUID & agent_id, + LLSD& stats_sd) { - TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, stats_sd); + TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, stats_sd); - cmdEnqueue(req); + cmdEnqueue(req); } // Threads: T* void LLTextureFetch::commandDataBreak() { - // The pedantically correct way to implement this is to create a command - // request object in the above fashion and enqueue it. However, this is - // simple data of an advisorial not operational nature and this case - // of shared-write access is tolerable. + // The pedantically correct way to implement this is to create a command + // request object in the above fashion and enqueue it. However, this is + // simple data of an advisorial not operational nature and this case + // of shared-write access is tolerable. - LLTextureFetch::svMetricsDataBreak = true; + LLTextureFetch::svMetricsDataBreak = true; } // Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { LL_PROFILE_ZONE_SCOPED; - lockQueue(); // +Mfq - mCommands.push_back(req); - unlockQueue(); // -Mfq + lockQueue(); // +Mfq + mCommands.push_back(req); + unlockQueue(); // -Mfq - unpause(); + unpause(); } // Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { LL_PROFILE_ZONE_SCOPED; - TFRequest * ret = 0; - - lockQueue(); // +Mfq - if (! mCommands.empty()) - { - ret = mCommands.front(); - mCommands.erase(mCommands.begin()); - } - unlockQueue(); // -Mfq + TFRequest * ret = 0; - return ret; + lockQueue(); // +Mfq + if (! mCommands.empty()) + { + ret = mCommands.front(); + mCommands.erase(mCommands.begin()); + } + unlockQueue(); // -Mfq + + return ret; } // Threads: Ttf void LLTextureFetch::cmdDoWork() { LL_PROFILE_ZONE_SCOPED; - if (mDebugPause) - { - return; // debug: don't do any work - } + if (mDebugPause) + { + return; // debug: don't do any work + } - TFRequest * req = cmdDequeue(); - if (req) - { - // One request per pass should really be enough for this. - req->doWork(this); - delete req; - } + TFRequest * req = cmdDequeue(); + if (req) + { + // One request per pass should really be enough for this. + req->doWork(this); + delete req; + } } ////////////////////////////////////////////////////////////////////////////// @@ -3531,23 +3531,23 @@ class AssetReportHandler : public LLCore::HttpHandler { public: - // Threads: Ttf - virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) - { - LLCore::HttpStatus status(response->getStatus()); - - if (status) - { - LL_DEBUGS(LOG_TXT) << "Successfully delivered asset metrics to grid." - << LL_ENDL; - } - else - { - LL_WARNS(LOG_TXT) << "Error delivering asset metrics to grid. Status: " - << status.toTerseString() - << ", Reason: " << status.toString() << LL_ENDL; - } - } + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) + { + LLCore::HttpStatus status(response->getStatus()); + + if (status) + { + LL_DEBUGS(LOG_TXT) << "Successfully delivered asset metrics to grid." + << LL_ENDL; + } + else + { + LL_WARNS(LOG_TXT) << "Error delivering asset metrics to grid. Status: " + << status.toTerseString() + << ", Reason: " << status.toString() << LL_ENDL; + } + } }; // end class AssetReportHandler /** @@ -3558,9 +3558,9 @@ public: bool TFReqSetRegion::doWork(LLTextureFetch *) { - LLViewerAssetStatsFF::set_region(mRegionHandle); + LLViewerAssetStatsFF::set_region(mRegionHandle); - return true; + return true; } TFReqSendMetrics::TFReqSendMetrics(const std::string & caps_url, @@ -3591,143 +3591,143 @@ bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { LL_PROFILE_ZONE_SCOPED; - - //if (! gViewerAssetStatsThread1) - // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); - - // In mStatsSD, we have a copy we own of the LLSD representation - // of the asset stats. Add some additional fields and ship it off. + //if (! gViewerAssetStatsThread1) + // return true; + + static volatile bool reporting_started(false); + static volatile S32 report_sequence(0); + + // In mStatsSD, we have a copy we own of the LLSD representation + // of the asset stats. Add some additional fields and ship it off. static const S32 metrics_data_version = 2; - - bool initial_report = !reporting_started; - mStatsSD["session_id"] = mSessionID; - mStatsSD["agent_id"] = mAgentID; - mStatsSD["message"] = "ViewerAssetMetrics"; - mStatsSD["sequence"] = report_sequence; - mStatsSD["initial"] = initial_report; - mStatsSD["version"] = metrics_data_version; - mStatsSD["break"] = static_cast<bool>(LLTextureFetch::svMetricsDataBreak); - - // Update sequence number - if (S32_MAX == ++report_sequence) - { - report_sequence = 0; - } - reporting_started = true; - - // Limit the size of the stats report if necessary. - - mStatsSD["truncated"] = truncate_viewer_metrics(10, mStatsSD); + + bool initial_report = !reporting_started; + mStatsSD["session_id"] = mSessionID; + mStatsSD["agent_id"] = mAgentID; + mStatsSD["message"] = "ViewerAssetMetrics"; + mStatsSD["sequence"] = report_sequence; + mStatsSD["initial"] = initial_report; + mStatsSD["version"] = metrics_data_version; + mStatsSD["break"] = static_cast<bool>(LLTextureFetch::svMetricsDataBreak); + + // Update sequence number + if (S32_MAX == ++report_sequence) + { + report_sequence = 0; + } + reporting_started = true; + + // Limit the size of the stats report if necessary. + + mStatsSD["truncated"] = truncate_viewer_metrics(10, mStatsSD); if (gSavedSettings.getBOOL("QAModeMetrics")) { dump_sequential_xml("metric_asset_stats",mStatsSD); } - - if (! mCapsURL.empty()) - { - // Don't care about handle, this is a fire-and-forget operation. - LLCoreHttpUtil::requestPostWithLLSD(&fetcher->getHttpRequest(), - fetcher->getMetricsPolicyClass(), - mCapsURL, - mStatsSD, - LLCore::HttpOptions::ptr_t(), - fetcher->getMetricsHeaders(), - mHandler); - LLTextureFetch::svMetricsDataBreak = false; - } - else - { - LLTextureFetch::svMetricsDataBreak = true; - } - - // In QA mode, Metrics submode, log the result for ease of testing - if (fetcher->isQAMode()) - { - LL_INFOS(LOG_TXT) << "ViewerAssetMetrics as submitted\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; - } - - return true; + + if (! mCapsURL.empty()) + { + // Don't care about handle, this is a fire-and-forget operation. + LLCoreHttpUtil::requestPostWithLLSD(&fetcher->getHttpRequest(), + fetcher->getMetricsPolicyClass(), + mCapsURL, + mStatsSD, + LLCore::HttpOptions::ptr_t(), + fetcher->getMetricsHeaders(), + mHandler); + LLTextureFetch::svMetricsDataBreak = false; + } + else + { + LLTextureFetch::svMetricsDataBreak = true; + } + + // In QA mode, Metrics submode, log the result for ease of testing + if (fetcher->isQAMode()) + { + LL_INFOS(LOG_TXT) << "ViewerAssetMetrics as submitted\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; + } + + return true; } bool truncate_viewer_metrics(int max_regions, LLSD & metrics) { - static const LLSD::String reg_tag("regions"); - static const LLSD::String duration_tag("duration"); - - LLSD & reg_map(metrics[reg_tag]); - if (reg_map.size() <= max_regions) - { - return false; - } - - // Build map of region hashes ordered by duration - typedef std::multimap<LLSD::Real, int> reg_ordered_list_t; - reg_ordered_list_t regions_by_duration; - - int ind(0); - LLSD::array_const_iterator it_end(reg_map.endArray()); - for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind) - { - LLSD::Real duration = (*it)[duration_tag].asReal(); - regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind)); - } - - // Build a replacement regions array with the longest-persistence regions - LLSD new_region(LLSD::emptyArray()); - reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend()); - reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin()); - for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2) - { - new_region.append(reg_map[it2->second]); - } - reg_map = new_region; - - return true; + static const LLSD::String reg_tag("regions"); + static const LLSD::String duration_tag("duration"); + + LLSD & reg_map(metrics[reg_tag]); + if (reg_map.size() <= max_regions) + { + return false; + } + + // Build map of region hashes ordered by duration + typedef std::multimap<LLSD::Real, int> reg_ordered_list_t; + reg_ordered_list_t regions_by_duration; + + int ind(0); + LLSD::array_const_iterator it_end(reg_map.endArray()); + for (LLSD::array_const_iterator it(reg_map.beginArray()); it_end != it; ++it, ++ind) + { + LLSD::Real duration = (*it)[duration_tag].asReal(); + regions_by_duration.insert(reg_ordered_list_t::value_type(duration, ind)); + } + + // Build a replacement regions array with the longest-persistence regions + LLSD new_region(LLSD::emptyArray()); + reg_ordered_list_t::const_reverse_iterator it2_end(regions_by_duration.rend()); + reg_ordered_list_t::const_reverse_iterator it2(regions_by_duration.rbegin()); + for (int i(0); i < max_regions && it2_end != it2; ++i, ++it2) + { + new_region.append(reg_map[it2->second]); + } + reg_map = new_region; + + return true; } } // end of anonymous namespace -LLTextureFetchTester::LLTextureFetchTester() : LLMetricPerformanceTesterBasic(sTesterName) +LLTextureFetchTester::LLTextureFetchTester() : LLMetricPerformanceTesterBasic(sTesterName) { - mTextureFetchTime = 0; - mSkippedStatesTime = 0; - mFileSize = 0; + mTextureFetchTime = 0; + mSkippedStatesTime = 0; + mFileSize = 0; } LLTextureFetchTester::~LLTextureFetchTester() { - outputTestResults(); - LLTextureFetch::sTesterp = NULL; + outputTestResults(); + LLTextureFetch::sTesterp = NULL; } -//virtual -void LLTextureFetchTester::outputTestRecord(LLSD *sd) -{ - std::string currentLabel = getCurrentLabelName(); +//virtual +void LLTextureFetchTester::outputTestRecord(LLSD *sd) +{ + std::string currentLabel = getCurrentLabelName(); - (*sd)[currentLabel]["Texture Fetch Time"] = (LLSD::Real)mTextureFetchTime; - (*sd)[currentLabel]["File Size"] = (LLSD::Integer)mFileSize; - (*sd)[currentLabel]["Skipped States Time"] = (LLSD::String)llformat("%.6f", mSkippedStatesTime); + (*sd)[currentLabel]["Texture Fetch Time"] = (LLSD::Real)mTextureFetchTime; + (*sd)[currentLabel]["File Size"] = (LLSD::Integer)mFileSize; + (*sd)[currentLabel]["Skipped States Time"] = (LLSD::String)llformat("%.6f", mSkippedStatesTime); - for(auto i : LOGGED_STATES) - { - (*sd)[currentLabel][sStateDescs[i]] = mStateTimersMap[i]; - } + for(auto i : LOGGED_STATES) + { + (*sd)[currentLabel][sStateDescs[i]] = mStateTimersMap[i]; + } } void LLTextureFetchTester::updateStats(const std::map<S32, F32> state_timers, const F32 fetch_time, const F32 skipped_states_time, const S32 file_size) { - mTextureFetchTime = fetch_time; - mStateTimersMap = state_timers; - mFileSize = file_size; - mSkippedStatesTime = skipped_states_time; - outputTestResults(); + mTextureFetchTime = fetch_time; + mStateTimersMap = state_timers; + mFileSize = file_size; + mSkippedStatesTime = skipped_states_time; + outputTestResults(); } |