diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/newview/lltexturefetch.cpp | 2049 |
1 files changed, 1570 insertions, 479 deletions
diff --git a/linden/indra/newview/lltexturefetch.cpp b/linden/indra/newview/lltexturefetch.cpp index 9344bbf..54b3f28 100644 --- a/linden/indra/newview/lltexturefetch.cpp +++ b/linden/indra/newview/lltexturefetch.cpp | |||
@@ -1,6 +1,6 @@ | |||
1 | /** | 1 | /** |
2 | * @file llviewerimage.cpp | 2 | * @file lltexturecache.cpp |
3 | * @brief Object which handles a received image (and associated texture(s)) | 3 | * @brief Object which fetches textures from the cache and/or network |
4 | * | 4 | * |
5 | * Copyright (c) 2000-2007, Linden Research, Inc. | 5 | * Copyright (c) 2000-2007, Linden Research, Inc. |
6 | * | 6 | * |
@@ -31,96 +31,290 @@ | |||
31 | 31 | ||
32 | #include "lltexturefetch.h" | 32 | #include "lltexturefetch.h" |
33 | 33 | ||
34 | #include "llworkerthread.h" | 34 | #include "llcurl.h" |
35 | #include "llhttpclient.h" | ||
35 | #include "llimage.h" | 36 | #include "llimage.h" |
36 | #include "llvfs.h" | 37 | #include "llimageworker.h" |
38 | #include "llworkerthread.h" | ||
37 | 39 | ||
40 | #include "llagent.h" | ||
41 | #include "lltexturecache.h" | ||
42 | #include "llviewerimagelist.h" | ||
38 | #include "llviewerimage.h" | 43 | #include "llviewerimage.h" |
44 | #include "llviewerregion.h" | ||
39 | #include "viewer.h" | 45 | #include "viewer.h" |
40 | 46 | ||
41 | ////////////////////////////////////////////////////////////////////////////// | 47 | ////////////////////////////////////////////////////////////////////////////// |
42 | 48 | ||
49 | //static | ||
43 | class LLTextureFetchWorker : public LLWorkerClass | 50 | class LLTextureFetchWorker : public LLWorkerClass |
44 | { | 51 | { |
45 | friend class LLTextureFetchImpl; | 52 | friend class LLTextureFetch; |
46 | |||
47 | public: | ||
48 | // From LLWorkerClass | ||
49 | static void initClass(bool threaded, bool runalways); | ||
50 | static void updateClass(); | ||
51 | static void cleanupClass(); | ||
52 | |||
53 | // New methods | ||
54 | static LLTextureFetchWorker* getWorker(const LLUUID& id, const LLHost& host, | ||
55 | F32 mPriority, S32 discard, | ||
56 | BOOL needs_aux = FALSE); | ||
57 | static LLTextureFetchWorker* getActiveWorker(const LLUUID& id); | ||
58 | 53 | ||
59 | private: | 54 | private: |
60 | static void sendRequestListToSimulators(); | 55 | class URLResponder : public LLHTTPClient::Responder |
56 | { | ||
57 | public: | ||
58 | URLResponder(LLTextureFetch* fetcher, const LLUUID& id) | ||
59 | : mFetcher(fetcher), mID(id) | ||
60 | { | ||
61 | } | ||
62 | ~URLResponder() | ||
63 | { | ||
64 | } | ||
65 | virtual void error(U32 status, const std::string& reason) | ||
66 | { | ||
67 | mFetcher->lockQueue(); | ||
68 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
69 | if (worker) | ||
70 | { | ||
71 | llinfos << "LLTextureFetchWorker::URLResponder::error " << status << ": " << reason << llendl; | ||
72 | worker->callbackURLReceived(LLSD(), false); | ||
73 | } | ||
74 | mFetcher->unlockQueue(); | ||
75 | } | ||
76 | virtual void result(const LLSD& content) | ||
77 | { | ||
78 | mFetcher->lockQueue(); | ||
79 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
80 | if (worker) | ||
81 | { | ||
82 | worker->callbackURLReceived(content, true); | ||
83 | } | ||
84 | mFetcher->unlockQueue(); | ||
85 | } | ||
86 | private: | ||
87 | LLTextureFetch* mFetcher; | ||
88 | LLUUID mID; | ||
89 | }; | ||
90 | |||
91 | class HTTPGetResponder : public LLCurl::Responder | ||
92 | { | ||
93 | public: | ||
94 | HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id) | ||
95 | : mFetcher(fetcher), mID(id) | ||
96 | { | ||
97 | } | ||
98 | ~HTTPGetResponder() | ||
99 | { | ||
100 | } | ||
101 | virtual void completed(U32 status, const std::stringstream& content) | ||
102 | { | ||
103 | mFetcher->lockQueue(); | ||
104 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
105 | if (worker) | ||
106 | { | ||
107 | const std::string& cstr = content.str(); | ||
108 | if (200 <= status && status < 300) | ||
109 | { | ||
110 | if (203 == status) // partial information (i.e. last block) | ||
111 | { | ||
112 | worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), true); | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | worker->callbackHttpGet((U8*)cstr.c_str(), cstr.length(), false); | ||
117 | } | ||
118 | } | ||
119 | else | ||
120 | { | ||
121 | llinfos << "LLTextureFetchWorker::HTTPGetResponder::error " << status << ": " << cstr << llendl; | ||
122 | worker->callbackHttpGet(NULL, -1, true); | ||
123 | } | ||
124 | } | ||
125 | mFetcher->unlockQueue(); | ||
126 | } | ||
127 | private: | ||
128 | LLTextureFetch* mFetcher; | ||
129 | LLUUID mID; | ||
130 | }; | ||
131 | |||
132 | class CacheReadResponder : public LLTextureCache::ReadResponder | ||
133 | { | ||
134 | public: | ||
135 | CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) | ||
136 | : mFetcher(fetcher), mID(id) | ||
137 | { | ||
138 | setImage(image); | ||
139 | } | ||
140 | virtual void completed(bool success) | ||
141 | { | ||
142 | mFetcher->lockQueue(); | ||
143 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
144 | if (worker) | ||
145 | { | ||
146 | worker->callbackCacheRead(success, mFormattedImage, mImageSize, mImageLocal); | ||
147 | } | ||
148 | mFetcher->unlockQueue(); | ||
149 | } | ||
150 | private: | ||
151 | LLTextureFetch* mFetcher; | ||
152 | LLUUID mID; | ||
153 | }; | ||
154 | |||
155 | class CacheWriteResponder : public LLTextureCache::WriteResponder | ||
156 | { | ||
157 | public: | ||
158 | CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) | ||
159 | : mFetcher(fetcher), mID(id) | ||
160 | { | ||
161 | } | ||
162 | virtual void completed(bool success) | ||
163 | { | ||
164 | mFetcher->lockQueue(); | ||
165 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
166 | if (worker) | ||
167 | { | ||
168 | worker->callbackCacheWrite(success); | ||
169 | } | ||
170 | mFetcher->unlockQueue(); | ||
171 | } | ||
172 | private: | ||
173 | LLTextureFetch* mFetcher; | ||
174 | LLUUID mID; | ||
175 | }; | ||
176 | |||
177 | class DecodeResponder : public LLResponder | ||
178 | { | ||
179 | public: | ||
180 | DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) | ||
181 | : mFetcher(fetcher), mID(id), mWorker(worker) | ||
182 | { | ||
183 | } | ||
184 | virtual void completed(bool success) | ||
185 | { | ||
186 | mFetcher->lockQueue(); | ||
187 | LLTextureFetchWorker* worker = mFetcher->getWorker(mID); | ||
188 | if (worker) | ||
189 | { | ||
190 | worker->callbackDecoded(success); | ||
191 | } | ||
192 | mFetcher->unlockQueue(); | ||
193 | } | ||
194 | private: | ||
195 | LLTextureFetch* mFetcher; | ||
196 | LLUUID mID; | ||
197 | LLTextureFetchWorker* mWorker; // debug only (may get deleted from under us, use mFetcher/mID) | ||
198 | }; | ||
199 | |||
200 | struct Compare | ||
201 | { | ||
202 | // lhs < rhs | ||
203 | bool operator()(const LLTextureFetchWorker* lhs, const LLTextureFetchWorker* rhs) const | ||
204 | { | ||
205 | // greater priority is "less" | ||
206 | const F32 lpriority = lhs->mImagePriority; | ||
207 | const F32 rpriority = rhs->mImagePriority; | ||
208 | if (lpriority > rpriority) // higher priority | ||
209 | return true; | ||
210 | else if (lpriority < rpriority) | ||
211 | return false; | ||
212 | else | ||
213 | return lhs < rhs; | ||
214 | } | ||
215 | }; | ||
61 | 216 | ||
62 | public: | 217 | public: |
63 | virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest() | 218 | /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() |
219 | /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) | ||
220 | /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) | ||
64 | 221 | ||
65 | ~LLTextureFetchWorker(); | 222 | ~LLTextureFetchWorker(); |
66 | void relese() { --mActiveCount; } | 223 | void relese() { --mActiveCount; } |
67 | 224 | ||
68 | |||
69 | protected: | 225 | protected: |
70 | LLTextureFetchWorker(const LLUUID& id, const LLHost& host, F32 mPriority, S32 discard); | 226 | LLTextureFetchWorker(LLTextureFetch* fetcher, const LLUUID& id, const LLHost& host, |
227 | F32 priority, S32 discard, S32 size); | ||
71 | 228 | ||
72 | private: | 229 | private: |
73 | virtual void startWork(S32 param); // called from addWork() (MAIN THREAD) | 230 | /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) |
74 | virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) | 231 | /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) |
75 | 232 | ||
233 | void resetFormattedData(); | ||
234 | |||
76 | void setImagePriority(F32 priority); | 235 | void setImagePriority(F32 priority); |
77 | void setDesiredDiscard(S32 discard); | 236 | void setDesiredDiscard(S32 discard, S32 size); |
78 | void insertPacket(S32 index, U8* data, S32 size); | 237 | bool insertPacket(S32 index, U8* data, S32 size); |
79 | void clearPackets(); | 238 | void clearPackets(); |
80 | U32 calcWorkPriority(); | 239 | U32 calcWorkPriority(); |
81 | bool startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type); | 240 | void removeFromCache(); |
82 | bool loadFromVFS(); | ||
83 | bool processSimulatorPackets(); | 241 | bool processSimulatorPackets(); |
84 | void startDecode(); | ||
85 | bool decodeImage(); | 242 | bool decodeImage(); |
86 | 243 | bool writeToCacheComplete(); | |
244 | |||
87 | void lockWorkData() { mWorkMutex.lock(); } | 245 | void lockWorkData() { mWorkMutex.lock(); } |
88 | void unlockWorkData() { mWorkMutex.unlock(); } | 246 | void unlockWorkData() { mWorkMutex.unlock(); } |
89 | 247 | ||
90 | static void lockQueue() { sDataMutex->lock(); } | 248 | void callbackURLReceived(const LLSD& data, bool success); |
91 | static void unlockQueue() { sDataMutex->unlock(); } | 249 | void callbackHttpGet(U8* data, S32 data_size, bool last_block); |
250 | void callbackCacheRead(bool success, LLImageFormatted* image, | ||
251 | S32 imagesize, BOOL islocal); | ||
252 | void callbackCacheWrite(bool success); | ||
253 | void callbackDecoded(bool success); | ||
92 | 254 | ||
93 | private: | 255 | private: |
94 | enum e_state | 256 | enum e_state // mState |
95 | { | 257 | { |
96 | INIT = 1, | 258 | // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) |
97 | LOAD_FROM_CACHE, | 259 | INVALID = 0, |
260 | INIT, | ||
261 | LOAD_FROM_TEXTURE_CACHE, | ||
262 | CACHE_POST, | ||
263 | LOAD_FROM_NETWORK, | ||
98 | LOAD_FROM_SIMULATOR, | 264 | LOAD_FROM_SIMULATOR, |
99 | WRITE_TO_VFS, | 265 | LOAD_FROM_HTTP_GET_URL, |
266 | LOAD_FROM_HTTP_GET_DATA, | ||
100 | DECODE_IMAGE, | 267 | DECODE_IMAGE, |
101 | DECODE_IMAGE_UPDATE, | 268 | DECODE_IMAGE_UPDATE, |
269 | WRITE_TO_CACHE, | ||
270 | WAIT_ON_WRITE, | ||
102 | DONE | 271 | DONE |
103 | }; | 272 | }; |
273 | enum e_request_state // mSentRequest | ||
274 | { | ||
275 | UNSENT = 0, | ||
276 | QUEUED = 1, | ||
277 | SENT_SIM = 2, | ||
278 | SENT_URL = 3, | ||
279 | SENT_HTTP = 4 | ||
280 | }; | ||
281 | static const char* sStateDescs[]; | ||
104 | e_state mState; | 282 | e_state mState; |
283 | LLTextureFetch* mFetcher; | ||
284 | LLImageWorker* mImageWorker; | ||
105 | LLPointer<LLImageFormatted> mFormattedImage; | 285 | LLPointer<LLImageFormatted> mFormattedImage; |
106 | LLPointer<LLImageRaw> mRawImage; | 286 | LLPointer<LLImageRaw> mRawImage; |
107 | LLPointer<LLImageRaw> mAuxImage; | 287 | LLPointer<LLImageRaw> mAuxImage; |
108 | LLUUID mID; | 288 | LLUUID mID; |
109 | LLHost mHost; | 289 | LLHost mHost; |
110 | F32 mPriority; | 290 | U8 mType; |
291 | F32 mImagePriority; | ||
292 | U32 mWorkPriority; | ||
293 | F32 mRequestedPriority; | ||
111 | S32 mDesiredDiscard; | 294 | S32 mDesiredDiscard; |
295 | S32 mSimRequestedDiscard; | ||
112 | S32 mRequestedDiscard; | 296 | S32 mRequestedDiscard; |
297 | S32 mLoadedDiscard; | ||
113 | S32 mDecodedDiscard; | 298 | S32 mDecodedDiscard; |
114 | LLFrameTimer mRequestedTimer; | 299 | LLFrameTimer mRequestedTimer; |
115 | LLFrameTimer mIdleTimer; | 300 | LLFrameTimer mFetchTimer; |
116 | LLVFSThread::handle_t mFileHandle; | 301 | LLTextureCache::handle_t mCacheReadHandle; |
302 | LLTextureCache::handle_t mCacheWriteHandle; | ||
117 | U8* mBuffer; | 303 | U8* mBuffer; |
118 | S32 mBufferSize; | 304 | S32 mBufferSize; |
119 | S32 mRequestedSize; | 305 | S32 mRequestedSize; |
306 | S32 mDesiredSize; | ||
307 | S32 mFileSize; | ||
308 | S32 mCachedSize; | ||
120 | BOOL mLoaded; | 309 | BOOL mLoaded; |
121 | BOOL mRequested; | 310 | e_request_state mSentRequest; |
122 | BOOL mDecoded; | 311 | BOOL mDecoded; |
312 | BOOL mWritten; | ||
123 | BOOL mNeedsAux; | 313 | BOOL mNeedsAux; |
314 | BOOL mHaveAllData; | ||
315 | BOOL mInLocalCache; | ||
316 | S32 mRetryAttempt; | ||
317 | std::string mURL; | ||
124 | S32 mActiveCount; | 318 | S32 mActiveCount; |
125 | 319 | ||
126 | // Work Data | 320 | // Work Data |
@@ -129,635 +323,1532 @@ private: | |||
129 | { | 323 | { |
130 | PacketData(U8* data, S32 size) { mData = data; mSize = size; } | 324 | PacketData(U8* data, S32 size) { mData = data; mSize = size; } |
131 | ~PacketData() { clearData(); } | 325 | ~PacketData() { clearData(); } |
132 | void clearData() { delete mData; mData = NULL; } | 326 | void clearData() { delete[] mData; mData = NULL; } |
133 | U8* mData; | 327 | U8* mData; |
134 | U32 mSize; | 328 | U32 mSize; |
135 | }; | 329 | }; |
136 | std::vector<PacketData*> mPackets; | 330 | std::vector<PacketData*> mPackets; |
331 | S32 mFirstPacket; | ||
137 | S32 mLastPacket; | 332 | S32 mLastPacket; |
138 | S32 mTotalPackets; | 333 | U16 mTotalPackets; |
139 | S32 mTotalBytes; | 334 | U8 mImageCodec; |
140 | 335 | }; | |
141 | // Class variables (statics) | ||
142 | |||
143 | static LLWorkerThread* sWorkerThread; | ||
144 | static LLMutex* sDataMutex; | ||
145 | 336 | ||
146 | // Map of all requests by UUID | 337 | //static |
147 | typedef std::map<LLUUID,LLTextureFetchWorker*> map_t; | 338 | const char* LLTextureFetchWorker::sStateDescs[] = { |
148 | static map_t sRequests; | 339 | "INVALID", |
340 | "INIT", | ||
341 | "LOAD_FROM_TEXTURE_CACHE", | ||
342 | "CACHE_POST", | ||
343 | "LOAD_FROM_NETWORK", | ||
344 | "LOAD_FROM_SIMULATOR", | ||
345 | "LOAD_FROM_HTTP_URL", | ||
346 | "LOAD_FROM_HTTP_DATA", | ||
347 | "DECODE_IMAGE", | ||
348 | "DECODE_IMAGE_UPDATE", | ||
349 | "WRITE_TO_CACHE", | ||
350 | "WAIT_ON_WRITE", | ||
351 | "DONE", | ||
352 | }; | ||
149 | 353 | ||
150 | // Set of requests that require network data | 354 | // called from MAIN THREAD |
151 | typedef std::set<LLTextureFetchWorker*> queue_t ; | ||
152 | static queue_t sNetworkQueue; | ||
153 | 355 | ||
154 | static LLFrameTimer sNetworkTimer; | 356 | LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, |
155 | }; | 357 | const LLUUID& id, // Image UUID |
358 | const LLHost& host, // Simulator host | ||
359 | F32 priority, // Priority | ||
360 | S32 discard, // Desired discard | ||
361 | S32 size) // Desired size | ||
362 | : LLWorkerClass(fetcher, "TextureFetch"), | ||
363 | mState(INIT), | ||
364 | mFetcher(fetcher), | ||
365 | mImageWorker(NULL), | ||
366 | mID(id), | ||
367 | mHost(host), | ||
368 | mImagePriority(priority), | ||
369 | mWorkPriority(0), | ||
370 | mRequestedPriority(0.f), | ||
371 | mDesiredDiscard(-1), | ||
372 | mSimRequestedDiscard(-1), | ||
373 | mRequestedDiscard(-1), | ||
374 | mLoadedDiscard(-1), | ||
375 | mDecodedDiscard(-1), | ||
376 | mCacheReadHandle(LLTextureCache::nullHandle()), | ||
377 | mCacheWriteHandle(LLTextureCache::nullHandle()), | ||
378 | mBuffer(NULL), | ||
379 | mBufferSize(0), | ||
380 | mRequestedSize(0), | ||
381 | mDesiredSize(FIRST_PACKET_SIZE), | ||
382 | mFileSize(0), | ||
383 | mCachedSize(0), | ||
384 | mLoaded(FALSE), | ||
385 | mSentRequest(UNSENT), | ||
386 | mDecoded(FALSE), | ||
387 | mWritten(FALSE), | ||
388 | mNeedsAux(FALSE), | ||
389 | mHaveAllData(FALSE), | ||
390 | mInLocalCache(FALSE), | ||
391 | mRetryAttempt(0), | ||
392 | mActiveCount(0), | ||
393 | mWorkMutex(fetcher->getWorkerAPRPool()), | ||
394 | mFirstPacket(0), | ||
395 | mLastPacket(-1), | ||
396 | mTotalPackets(0), | ||
397 | mImageCodec(IMG_CODEC_INVALID) | ||
398 | { | ||
399 | calcWorkPriority(); | ||
400 | mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; | ||
401 | // llinfos << "Create: " << mID << " mHost:" << host << " Discard=" << discard << llendl; | ||
402 | if (!mFetcher->mDebugPause) | ||
403 | { | ||
404 | U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; | ||
405 | addWork(0, work_priority ); | ||
406 | } | ||
407 | setDesiredDiscard(discard, size); | ||
408 | } | ||
156 | 409 | ||
157 | //statics | 410 | LLTextureFetchWorker::~LLTextureFetchWorker() |
158 | LLTextureFetchWorker::map_t LLTextureFetchWorker::sRequests; | 411 | { |
159 | LLTextureFetchWorker::queue_t LLTextureFetchWorker::sNetworkQueue; | 412 | // llinfos << "Destroy: " << mID |
160 | LLFrameTimer LLTextureFetchWorker::sNetworkTimer; | 413 | // << " Decoded=" << mDecodedDiscard |
161 | LLWorkerThread* LLTextureFetchWorker::sWorkerThread = NULL; | 414 | // << " Requested=" << mRequestedDiscard |
162 | LLMutex* LLTextureFetchWorker::sDataMutex = NULL; | 415 | // << " Desired=" << mDesiredDiscard << llendl; |
416 | llassert_always(!haveWork()); | ||
417 | lockWorkData(); | ||
418 | if (mCacheReadHandle != LLTextureCache::nullHandle()) | ||
419 | { | ||
420 | mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); | ||
421 | } | ||
422 | if (mCacheWriteHandle != LLTextureCache::nullHandle()) | ||
423 | { | ||
424 | mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); | ||
425 | } | ||
426 | if (mImageWorker) | ||
427 | { | ||
428 | mImageWorker->scheduleDelete(); | ||
429 | } | ||
430 | mFormattedImage = NULL; | ||
431 | clearPackets(); | ||
432 | unlockWorkData(); | ||
433 | } | ||
163 | 434 | ||
164 | // called from MAIN THREAD | 435 | void LLTextureFetchWorker::clearPackets() |
165 | //static | ||
166 | void LLTextureFetchWorker::initClass(bool threaded, bool runalways) | ||
167 | { | 436 | { |
168 | sWorkerThread = new LLWorkerThread(threaded, runalways); | 437 | for_each(mPackets.begin(), mPackets.end(), DeletePointer()); |
169 | sDataMutex = new LLMutex(sWorkerThread->getAPRPool()); | 438 | mPackets.clear(); |
439 | mTotalPackets = 0; | ||
440 | mLastPacket = -1; | ||
441 | mFirstPacket = 0; | ||
170 | } | 442 | } |
171 | 443 | ||
172 | // called from MAIN THREAD | 444 | U32 LLTextureFetchWorker::calcWorkPriority() |
173 | //static | ||
174 | void LLTextureFetchWorker::updateClass() | ||
175 | { | 445 | { |
176 | const F32 REQUEST_TIME = 1.f; | 446 | // llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerImage::maxDecodePriority()); |
177 | const F32 MIN_IDLE_TIME = 1.f * 60.f; // 1 minute | 447 | F32 priority_scale = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerImage::maxDecodePriority(); |
178 | const F32 MAX_IDLE_TIME = 5.f * 60.f; // 5 minutes | 448 | mWorkPriority = (U32)(mImagePriority * priority_scale); |
179 | const S32 MIN_IDLE_COUNT = 16; // always keep last 16 idle requests | 449 | return mWorkPriority; |
180 | const F32 MAX_IDLE_COUNT = 1024; // max number of idle requests | 450 | } |
181 | 451 | ||
182 | // Periodically, gather the list of textures that need data from the network | 452 | // mWorkMutex is locked |
183 | // And send the requests out to the simulators | 453 | void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) |
184 | if (sNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME) | 454 | { |
455 | bool prioritize = false; | ||
456 | if (mDesiredDiscard != discard) | ||
185 | { | 457 | { |
186 | sNetworkTimer.reset(); | 458 | if (!haveWork()) |
187 | sendRequestListToSimulators(); | 459 | { |
460 | calcWorkPriority(); | ||
461 | if (!mFetcher->mDebugPause) | ||
462 | { | ||
463 | U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; | ||
464 | addWork(0, work_priority); | ||
465 | } | ||
466 | } | ||
467 | else if (mDesiredDiscard < discard) | ||
468 | { | ||
469 | prioritize = true; | ||
470 | } | ||
471 | mDesiredDiscard = discard; | ||
472 | mDesiredSize = size; | ||
188 | } | 473 | } |
189 | // Remove any old requests (releasing their raw data) | 474 | else if (size > mDesiredSize) |
190 | typedef std::pair<F32, LLTextureFetchWorker*> idle_pair; | ||
191 | typedef std::set<idle_pair, compare_pair_greater<F32,LLTextureFetchWorker*> > idle_set; | ||
192 | idle_set remove_set; | ||
193 | for (map_t::iterator iter = sRequests.begin(); iter != sRequests.end(); ++iter) | ||
194 | { | 475 | { |
195 | LLTextureFetchWorker* worker = iter->second; | 476 | mDesiredSize = size; |
196 | if (worker->mActiveCount > 0) | 477 | prioritize = true; |
197 | continue; | ||
198 | if (worker->haveWork()) | ||
199 | continue; | ||
200 | F32 idletime = worker->mIdleTimer.getElapsedTimeF32(); | ||
201 | if (idletime < MIN_IDLE_TIME) | ||
202 | continue; | ||
203 | remove_set.insert(std::make_pair(idletime, worker)); | ||
204 | } | 478 | } |
205 | S32 num_left = remove_set.size(); | 479 | if ((prioritize && mState == INIT) || mState == DONE) |
206 | for (idle_set::iterator iter = remove_set.begin(); iter != remove_set.end(); ++iter) | ||
207 | { | 480 | { |
208 | if (num_left <= MIN_IDLE_COUNT) | 481 | mState = INIT; |
209 | break; | 482 | U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; |
210 | if (iter->first < MAX_IDLE_TIME && | 483 | setPriority(work_priority); |
211 | num_left < MAX_IDLE_COUNT) | ||
212 | break; | ||
213 | num_left--; | ||
214 | } | 484 | } |
215 | } | 485 | } |
216 | 486 | ||
217 | // called from MAIN THREAD | 487 | void LLTextureFetchWorker::setImagePriority(F32 priority) |
218 | //static | ||
219 | void LLTextureFetchWorker::sendRequestListToSimulators() | ||
220 | { | 488 | { |
221 | const F32 LAZY_FLUSH_TIMEOUT = 60.f; | 489 | // llassert_always(priority >= 0 && priority <= LLViewerImage::maxDecodePriority()); |
222 | const S32 IMAGES_PER_REQUEST = 50; | 490 | F32 delta = fabs(priority - mImagePriority); |
223 | // Send requests | 491 | if (delta > (mImagePriority * .05f) || mState == DONE) |
224 | typedef std::map< LLHost, std::vector<LLTextureFetchWorker*> > work_request_map_t; | ||
225 | work_request_map_t requests; | ||
226 | for (queue_t::iterator iter = sNetworkQueue.begin(); iter != sNetworkQueue.end(); ++iter) | ||
227 | { | 492 | { |
228 | LLTextureFetchWorker* req = *iter; | 493 | mImagePriority = priority; |
229 | if (req->haveWork()) | 494 | calcWorkPriority(); |
495 | U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); | ||
496 | setPriority(work_priority); | ||
497 | } | ||
498 | } | ||
499 | |||
500 | void LLTextureFetchWorker::resetFormattedData() | ||
501 | { | ||
502 | delete[] mBuffer; | ||
503 | mBuffer = NULL; | ||
504 | mBufferSize = 0; | ||
505 | if (mFormattedImage.notNull()) | ||
506 | { | ||
507 | mFormattedImage->deleteData(); | ||
508 | } | ||
509 | mHaveAllData = FALSE; | ||
510 | } | ||
511 | |||
512 | // Called from MAIN thread | ||
513 | void LLTextureFetchWorker::startWork(S32 param) | ||
514 | { | ||
515 | llassert(mImageWorker == NULL); | ||
516 | llassert(mFormattedImage.isNull()); | ||
517 | } | ||
518 | |||
519 | #include "llviewerimagelist.h" // debug | ||
520 | |||
521 | // Called from LLWorkerThread::processRequest() | ||
522 | bool LLTextureFetchWorker::doWork(S32 param) | ||
523 | { | ||
524 | LLMutexLock lock(&mWorkMutex); | ||
525 | |||
526 | if (mFetcher->mDebugPause) | ||
527 | { | ||
528 | return false; // debug: don't do any work | ||
529 | } | ||
530 | if (mID == mFetcher->mDebugID) | ||
531 | { | ||
532 | mFetcher->mDebugCount++; // for setting breakpoints | ||
533 | } | ||
534 | |||
535 | if (mState != DONE) | ||
536 | { | ||
537 | mFetchTimer.reset(); | ||
538 | } | ||
539 | |||
540 | if (mState == INIT) | ||
541 | { | ||
542 | mRequestedDiscard = -1; | ||
543 | mLoadedDiscard = -1; | ||
544 | mDecodedDiscard = -1; | ||
545 | mRequestedSize = 0; | ||
546 | mFileSize = 0; | ||
547 | mCachedSize = 0; | ||
548 | mLoaded = FALSE; | ||
549 | mSentRequest = UNSENT; | ||
550 | mDecoded = FALSE; | ||
551 | mWritten = FALSE; | ||
552 | delete[] mBuffer; | ||
553 | mBuffer = NULL; | ||
554 | mBufferSize = 0; | ||
555 | mHaveAllData = FALSE; | ||
556 | clearPackets(); // TODO: Shouldn't be necessary | ||
557 | mCacheReadHandle = LLTextureCache::nullHandle(); | ||
558 | mCacheWriteHandle = LLTextureCache::nullHandle(); | ||
559 | mURL.clear(); | ||
560 | mState = LOAD_FROM_TEXTURE_CACHE; | ||
561 | // fall through | ||
562 | } | ||
563 | |||
564 | if (mState == LOAD_FROM_TEXTURE_CACHE) | ||
565 | { | ||
566 | if (mCacheReadHandle == LLTextureCache::nullHandle()) | ||
230 | { | 567 | { |
231 | continue; // busy | 568 | U32 cache_priority = mWorkPriority; |
569 | S32 offset = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; | ||
570 | S32 size = mDesiredSize - offset; | ||
571 | if (size <= 0) | ||
572 | { | ||
573 | mState = CACHE_POST; | ||
574 | return false; | ||
575 | } | ||
576 | mFileSize = 0; | ||
577 | mLoaded = FALSE; | ||
578 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it | ||
579 | CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); | ||
580 | mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, | ||
581 | offset, size, responder); | ||
232 | } | 582 | } |
233 | if ((req->mRequestedDiscard == req->mDesiredDiscard) && | 583 | |
234 | (req->mRequestedTimer.getElapsedTimeF32() < LAZY_FLUSH_TIMEOUT)) | 584 | if (mLoaded) |
235 | { | 585 | { |
236 | continue; | 586 | // Make sure request is complete. *TODO: make this auto-complete |
587 | if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false)) | ||
588 | { | ||
589 | mCacheReadHandle = LLTextureCache::nullHandle(); | ||
590 | mState = CACHE_POST; | ||
591 | // fall through | ||
592 | } | ||
593 | else | ||
594 | { | ||
595 | return false; | ||
596 | } | ||
597 | } | ||
598 | else | ||
599 | { | ||
600 | return false; | ||
237 | } | 601 | } |
238 | req->mRequestedDiscard = req->mDesiredDiscard; | ||
239 | req->mRequestedTimer.reset(); | ||
240 | requests[req->mHost].push_back(req); | ||
241 | } | 602 | } |
242 | for (work_request_map_t::iterator iter1 = requests.begin(); | 603 | |
243 | iter1 != requests.end(); ++iter1) | 604 | if (mState == CACHE_POST) |
244 | { | 605 | { |
245 | LLHost host = iter1->first; | 606 | mCachedSize = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; |
246 | // invalid host = load from static VFS | 607 | // Successfully loaded |
247 | if (host != LLHost::invalid) | 608 | if ((mCachedSize >= mDesiredSize) || mHaveAllData) |
248 | { | 609 | { |
249 | S32 request_count = 0; | 610 | // we have enough data, decode it |
250 | for (std::vector<LLTextureFetchWorker*>::iterator iter2 = iter1->second.begin(); | 611 | llassert_always(mFormattedImage->getDataSize() > 0); |
251 | iter2 != iter1->second.end(); ++iter2) | 612 | mState = DECODE_IMAGE; |
613 | // fall through | ||
614 | } | ||
615 | else | ||
616 | { | ||
617 | // need more data | ||
618 | mState = LOAD_FROM_NETWORK; | ||
619 | // fall through | ||
620 | } | ||
621 | } | ||
622 | |||
623 | if (mState == LOAD_FROM_NETWORK) | ||
624 | { | ||
625 | if (mSentRequest == UNSENT) | ||
626 | { | ||
627 | if (mFormattedImage.isNull()) | ||
252 | { | 628 | { |
253 | LLTextureFetchWorker* req = *iter2; | 629 | mFormattedImage = new LLImageJ2C; |
254 | if (0 == request_count) | 630 | } |
631 | // Add this to the network queue and sit here. | ||
632 | // LLTextureFetch::update() will send off a request which will change our state | ||
633 | S32 data_size = mFormattedImage->getDataSize(); | ||
634 | if (data_size > 0) | ||
635 | { | ||
636 | // Only used for simulator requests | ||
637 | mFirstPacket = (data_size - FIRST_PACKET_SIZE) / MAX_IMG_PACKET_SIZE + 1; | ||
638 | if (FIRST_PACKET_SIZE + (mFirstPacket-1) * MAX_IMG_PACKET_SIZE != data_size) | ||
255 | { | 639 | { |
256 | gMessageSystem->newMessageFast(_PREHASH_RequestImage); | 640 | llwarns << "Bad CACHED TEXTURE size: " << data_size << " removing." << llendl; |
641 | removeFromCache(); | ||
642 | resetFormattedData(); | ||
643 | clearPackets(); | ||
257 | } | 644 | } |
258 | S32 packet = req->mLastPacket + 1; | 645 | else |
259 | gMessageSystem->nextBlockFast(_PREHASH_RequestImage); | 646 | { |
260 | gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID); | 647 | mLastPacket = mFirstPacket-1; |
261 | gMessageSystem->addS32Fast(_PREHASH_DiscardLevel, req->mDesiredDiscard); | 648 | mTotalPackets = (mFileSize - FIRST_PACKET_SIZE + MAX_IMG_PACKET_SIZE-1) / MAX_IMG_PACKET_SIZE + 1; |
262 | gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mPriority); | 649 | } |
263 | gMessageSystem->addU32Fast(_PREHASH_Packet, packet); | 650 | } |
651 | mRequestedSize = mDesiredSize; | ||
652 | mRequestedDiscard = mDesiredDiscard; | ||
653 | mSentRequest = QUEUED; | ||
654 | mFetcher->lockQueue(); | ||
655 | mFetcher->addToNetworkQueue(this); | ||
656 | mFetcher->unlockQueue(); | ||
657 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
658 | } | ||
659 | return false; | ||
660 | } | ||
661 | |||
662 | if (mState == LOAD_FROM_SIMULATOR) | ||
663 | { | ||
664 | if (processSimulatorPackets()) | ||
665 | { | ||
666 | mFetcher->lockQueue(); | ||
667 | mFetcher->removeFromNetworkQueue(this); | ||
668 | mFetcher->unlockQueue(); | ||
669 | if (!mFormattedImage->getDataSize()) | ||
670 | { | ||
671 | // processSimulatorPackets() failed | ||
672 | llwarns << "processSimulatorPackets() failed to load buffer" << llendl; | ||
673 | return true; // failed | ||
674 | } | ||
675 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
676 | mState = DECODE_IMAGE; | ||
677 | } | ||
678 | else | ||
679 | { | ||
680 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
681 | } | ||
682 | return false; | ||
683 | } | ||
684 | |||
685 | #if 0 | ||
686 | if (mState == LOAD_FROM_HTTP_GET_URL) | ||
687 | { | ||
688 | if (!mSentRequest) | ||
689 | { | ||
690 | mSentRequest = TRUE; | ||
691 | mLoaded = FALSE; | ||
692 | std::string url; | ||
693 | LLViewerRegion* region = gAgent.getRegion(); | ||
694 | if (region) | ||
695 | { | ||
696 | url = region->getCapability("RequestTextureDownload"); | ||
697 | } | ||
698 | if (!url.empty()) | ||
699 | { | ||
700 | LLSD sd; | ||
701 | sd = mID.asString(); | ||
702 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
703 | LLHTTPClient::post(url, sd, new URLResponder(mFetcher, mID)); | ||
704 | return false; | ||
705 | } | ||
706 | else | ||
707 | { | ||
708 | llwarns << mID << ": HTTP get url failed, requesting from simulator" << llendl; | ||
709 | mSentRequest = FALSE; | ||
710 | mState = LOAD_FROM_SIMULATOR; | ||
711 | return false; | ||
712 | } | ||
713 | } | ||
714 | else | ||
715 | { | ||
716 | if (mLoaded) | ||
717 | { | ||
718 | if (!mURL.empty()) | ||
719 | { | ||
720 | mState = LOAD_FROM_HTTP_GET_DATA; | ||
721 | mSentRequest = FALSE; // reset | ||
722 | mLoaded = FALSE; // reset | ||
723 | } | ||
724 | else | ||
725 | { | ||
726 | llwarns << mID << ": HTTP get url is empty, requesting from simulator" << llendl; | ||
727 | mSentRequest = FALSE; | ||
728 | mState = LOAD_FROM_SIMULATOR; | ||
729 | return false; | ||
730 | } | ||
731 | } | ||
732 | } | ||
733 | // fall through | ||
734 | } | ||
735 | |||
736 | if (mState == LOAD_FROM_HTTP_GET_DATA) | ||
737 | { | ||
738 | if (!mSentRequest) | ||
739 | { | ||
740 | mSentRequest = TRUE; | ||
741 | S32 cur_size = mFormattedImage->getDataSize(); // amount of data we already have | ||
742 | mRequestedSize = mDesiredSize; | ||
743 | mRequestedDiscard = mDesiredDiscard; | ||
744 | #if 1 // *TODO: LLCurl::getByteRange is broken (ignores range) | ||
745 | cur_size = 0; | ||
746 | mFormattedImage->deleteData(); | ||
747 | #endif | ||
748 | mRequestedSize -= cur_size; | ||
749 | // F32 priority = mImagePriority / (F32)LLViewerImage::maxDecodePriority(); // 0-1 | ||
750 | S32 offset = cur_size; | ||
751 | mBufferSize = cur_size; // This will get modified by callbackHttpGet() | ||
752 | std::string url; | ||
753 | if (mURL.empty()) | ||
754 | { | ||
755 | //url = "http://asset.agni/0000002f-38ae-0e17-8e72-712e58964e9c.texture"; | ||
756 | std::stringstream urlstr; | ||
757 | urlstr << "http://asset.agni/" << mID.asString() << ".texture"; | ||
758 | url = urlstr.str(); | ||
759 | } | ||
760 | else | ||
761 | { | ||
762 | url = mURL; | ||
763 | } | ||
764 | mLoaded = FALSE; | ||
765 | // llinfos << "HTTP GET: " << mID << " Offset: " << offset << " Bytes: " << mRequestedSize << llendl; | ||
766 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
767 | LLCurl::getByteRange(url, offset, mRequestedSize, | ||
768 | new HTTPGetResponder(mFetcher, mID)); // *TODO: use mWorkPriority | ||
769 | return false; // not done | ||
770 | } | ||
264 | 771 | ||
265 | request_count++; | 772 | if (mLoaded) |
266 | if (request_count >= IMAGES_PER_REQUEST) | 773 | { |
774 | S32 cur_size = mFormattedImage->getDataSize(); | ||
775 | if (mRequestedSize < 0) | ||
776 | { | ||
777 | llwarns << "http get failed for: " << mID << llendl; | ||
778 | if (cur_size == 0) | ||
267 | { | 779 | { |
268 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | 780 | resetFormattedData(); |
269 | request_count = 0; | 781 | return true; // failed |
782 | } | ||
783 | else | ||
784 | { | ||
785 | mState = DECODE_IMAGE; | ||
786 | return false; // use what we have | ||
270 | } | 787 | } |
271 | } | 788 | } |
272 | if (request_count >= IMAGES_PER_REQUEST) | 789 | llassert_always(mBufferSize == cur_size + mRequestedSize); |
790 | if (mHaveAllData) | ||
273 | { | 791 | { |
274 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | 792 | mFileSize = mBufferSize; |
275 | } | 793 | } |
794 | U8* buffer = new U8[mBufferSize]; | ||
795 | if (cur_size > 0) | ||
796 | { | ||
797 | memcpy(buffer, mFormattedImage->getData(), cur_size); | ||
798 | } | ||
799 | memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append | ||
800 | // NOTE: setData releases current data and owns new data (buffer) | ||
801 | mFormattedImage->setData(buffer, mBufferSize); | ||
802 | // delete temp data | ||
803 | delete[] mBuffer; // Note: not 'buffer' (assigned in setData()) | ||
804 | mBuffer = NULL; | ||
805 | mBufferSize = 0; | ||
806 | mLoadedDiscard = mRequestedDiscard; | ||
807 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
808 | mState = DECODE_IMAGE; | ||
809 | return false; | ||
810 | } | ||
811 | |||
812 | // NOTE: Priority gets updated when the http get completes (in callbackHTTPGet()) | ||
813 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
814 | return false; | ||
815 | } | ||
816 | #endif | ||
817 | |||
818 | if (mState == DECODE_IMAGE) | ||
819 | { | ||
820 | llassert_always(mFormattedImage->getDataSize() > 0); | ||
821 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it | ||
822 | mRawImage = NULL; | ||
823 | mAuxImage = NULL; | ||
824 | llassert_always(mImageWorker == NULL); | ||
825 | llassert_always(mFormattedImage.notNull()); | ||
826 | S32 discard = mHaveAllData ? 0 : mLoadedDiscard; | ||
827 | U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; | ||
828 | mDecoded = FALSE; | ||
829 | mState = DECODE_IMAGE_UPDATE; | ||
830 | mImageWorker = new LLImageWorker(mFormattedImage, image_priority, discard, new DecodeResponder(mFetcher, mID, this)); | ||
831 | // fall though (need to call requestDecodedData() to start work) | ||
832 | } | ||
833 | |||
834 | if (mState == DECODE_IMAGE_UPDATE) | ||
835 | { | ||
836 | if (decodeImage()) | ||
837 | { | ||
838 | if (mDecodedDiscard < 0) | ||
839 | { | ||
840 | if (mCachedSize > 0 && !mInLocalCache && mRetryAttempt == 0) | ||
841 | { | ||
842 | // Cache file should be deleted, try again | ||
843 | llwarns << mID << ": Decode of cached file failed (removed), retrying" << llendl; | ||
844 | mFormattedImage = NULL; | ||
845 | ++mRetryAttempt; | ||
846 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
847 | mState = INIT; | ||
848 | return false; | ||
849 | } | ||
850 | else | ||
851 | { | ||
852 | llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; | ||
853 | mState = DONE; // failed | ||
854 | } | ||
855 | } | ||
856 | else | ||
857 | { | ||
858 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
859 | mState = WRITE_TO_CACHE; | ||
860 | } | ||
861 | // fall through | ||
862 | } | ||
863 | else | ||
864 | { | ||
865 | return false; | ||
276 | } | 866 | } |
277 | } | 867 | } |
278 | } | ||
279 | 868 | ||
280 | //static | 869 | if (mState == WRITE_TO_CACHE) |
281 | LLTextureFetchWorker* LLTextureFetchWorker::getWorker(const LLUUID& id, | ||
282 | const LLHost& host, | ||
283 | F32 priority, | ||
284 | S32 discard, | ||
285 | BOOL needs_aux) | ||
286 | { | ||
287 | LLTextureFetchWorker* res; | ||
288 | lockQueue(); | ||
289 | map_t::iterator iter = sRequests.find(id); | ||
290 | if (iter != sRequests.end()) | ||
291 | { | 870 | { |
292 | res = iter->second; | 871 | if (mInLocalCache || !mFileSize || mSentRequest == UNSENT) |
293 | if (res->mHost != host) | ||
294 | { | 872 | { |
295 | llerrs << "LLTextureFetchWorker::getWorker called with multiple hosts" << llendl; | 873 | // If we're in a local cache or we didn't actually receive any new data, skip |
874 | mState = DONE; | ||
875 | return false; | ||
296 | } | 876 | } |
297 | res->setImagePriority(priority); | 877 | S32 datasize = mFormattedImage->getDataSize(); |
298 | res->setDesiredDiscard(discard); | 878 | llassert_always(datasize); |
299 | 879 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it | |
880 | U32 cache_priority = mWorkPriority; | ||
881 | mWritten = FALSE; | ||
882 | mState = WAIT_ON_WRITE; | ||
883 | CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); | ||
884 | mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, | ||
885 | mFormattedImage->getData(), datasize, | ||
886 | mFileSize, responder); | ||
887 | // fall through | ||
300 | } | 888 | } |
301 | else | 889 | |
890 | if (mState == WAIT_ON_WRITE) | ||
302 | { | 891 | { |
303 | res = new LLTextureFetchWorker(id, host, priority, discard); | 892 | if (writeToCacheComplete()) |
893 | { | ||
894 | mState = DONE; | ||
895 | // fall through | ||
896 | } | ||
897 | else | ||
898 | { | ||
899 | if (mDesiredDiscard < mDecodedDiscard) | ||
900 | { | ||
901 | // We're waiting for this write to complete before we can receive more data | ||
902 | // (we can't touch mFormattedImage until the write completes) | ||
903 | // Prioritize the write | ||
904 | mFetcher->mTextureCache->prioritizeWrite(mCacheWriteHandle); | ||
905 | } | ||
906 | return false; | ||
907 | } | ||
304 | } | 908 | } |
305 | unlockQueue(); | ||
306 | res->mActiveCount++; | ||
307 | res->mNeedsAux = needs_aux; | ||
308 | return res; | ||
309 | } | ||
310 | 909 | ||
311 | LLTextureFetchWorker* LLTextureFetchWorker::getActiveWorker(const LLUUID& id) | 910 | if (mState == DONE) |
312 | { | ||
313 | LLTextureFetchWorker* res = NULL; | ||
314 | lockQueue(); | ||
315 | map_t::iterator iter = sRequests.find(id); | ||
316 | if (iter != sRequests.end()) | ||
317 | { | 911 | { |
318 | res = iter->second; | 912 | if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) |
913 | { | ||
914 | // More data was requested, return to INIT | ||
915 | mState = INIT; | ||
916 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
917 | return false; | ||
918 | } | ||
919 | else | ||
920 | { | ||
921 | setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); | ||
922 | return true; | ||
923 | } | ||
319 | } | 924 | } |
320 | unlockQueue(); | 925 | |
321 | return res; | 926 | return false; |
322 | } | 927 | } |
323 | 928 | ||
324 | LLTextureFetchWorker::LLTextureFetchWorker(const LLUUID& id, // Image UUID | 929 | // Called from MAIN thread |
325 | const LLHost& host, // Simulator host | 930 | void LLTextureFetchWorker::endWork(S32 param, bool aborted) |
326 | F32 priority, // Priority | ||
327 | S32 discard) // Desired discard level | ||
328 | : LLWorkerClass(sWorkerThread, "TextureFetch"), | ||
329 | mState(INIT), | ||
330 | mID(id), | ||
331 | mHost(host), | ||
332 | mPriority(priority), | ||
333 | mDesiredDiscard(discard), | ||
334 | mRequestedDiscard(-1), | ||
335 | mDecodedDiscard(-1), | ||
336 | mFileHandle(LLVFSThread::nullHandle()), | ||
337 | mBuffer(NULL), | ||
338 | mBufferSize(0), | ||
339 | mRequestedSize(0), | ||
340 | mLoaded(FALSE), | ||
341 | mRequested(FALSE), | ||
342 | mDecoded(FALSE), | ||
343 | mActiveCount(0), | ||
344 | mWorkMutex(sWorkerThread->getAPRPool()), | ||
345 | mLastPacket(-1), | ||
346 | mTotalPackets(0), | ||
347 | mTotalBytes(0) | ||
348 | { | 931 | { |
349 | lockQueue(); | 932 | if (mImageWorker) |
350 | sRequests[mID] = this; | 933 | { |
351 | unlockQueue(); | 934 | mImageWorker->scheduleDelete(); |
352 | addWork(0, calcWorkPriority()); | 935 | mImageWorker = NULL; |
936 | } | ||
937 | mFormattedImage = NULL; | ||
353 | } | 938 | } |
354 | 939 | ||
355 | LLTextureFetchWorker::~LLTextureFetchWorker() | 940 | ////////////////////////////////////////////////////////////////////////////// |
941 | |||
942 | // virtual | ||
943 | void LLTextureFetchWorker::finishWork(S32 param, bool completed) | ||
356 | { | 944 | { |
357 | lockQueue(); | 945 | // The following are required in case the work was aborted |
358 | mFormattedImage = NULL; | 946 | if (mCacheReadHandle != LLTextureCache::nullHandle()) |
359 | map_t::iterator iter = sRequests.find(mID); | ||
360 | if (iter != sRequests.end() && iter->second == this) | ||
361 | { | 947 | { |
362 | sRequests.erase(iter); | 948 | mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); |
949 | mCacheReadHandle = LLTextureCache::nullHandle(); | ||
950 | } | ||
951 | if (mCacheWriteHandle != LLTextureCache::nullHandle()) | ||
952 | { | ||
953 | mFetcher->mTextureCache->writeComplete(mCacheWriteHandle, true); | ||
954 | mCacheWriteHandle = LLTextureCache::nullHandle(); | ||
363 | } | 955 | } |
364 | sNetworkQueue.erase(this); | ||
365 | unlockQueue(); | ||
366 | clearPackets(); | ||
367 | } | 956 | } |
368 | 957 | ||
369 | void LLTextureFetchWorker::clearPackets() | 958 | // virtual |
959 | bool LLTextureFetchWorker::deleteOK() | ||
370 | { | 960 | { |
371 | lockWorkData(); | 961 | bool delete_ok = true; |
372 | for_each(mPackets.begin(), mPackets.end(), DeletePointer()); | 962 | // Allow any pending reads or writes to complete |
373 | mPackets.clear(); | 963 | if (mCacheReadHandle != LLTextureCache::nullHandle()) |
374 | unlockWorkData(); | 964 | { |
965 | if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) | ||
966 | { | ||
967 | mCacheReadHandle = LLTextureCache::nullHandle(); | ||
968 | } | ||
969 | else | ||
970 | { | ||
971 | delete_ok = false; | ||
972 | } | ||
973 | } | ||
974 | if (mCacheWriteHandle != LLTextureCache::nullHandle()) | ||
975 | { | ||
976 | if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) | ||
977 | { | ||
978 | mCacheWriteHandle = LLTextureCache::nullHandle(); | ||
979 | } | ||
980 | else | ||
981 | { | ||
982 | delete_ok = false; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | if ((haveWork() && | ||
987 | // not ok to delete from these states | ||
988 | ((mState >= LOAD_FROM_HTTP_GET_URL && mState <= LOAD_FROM_HTTP_GET_DATA) || | ||
989 | (mState >= WRITE_TO_CACHE && mState <= WAIT_ON_WRITE)))) | ||
990 | { | ||
991 | delete_ok = false; | ||
992 | } | ||
993 | |||
994 | return delete_ok; | ||
375 | } | 995 | } |
376 | 996 | ||
377 | U32 LLTextureFetchWorker::calcWorkPriority() | 997 | |
998 | void LLTextureFetchWorker::removeFromCache() | ||
378 | { | 999 | { |
379 | F32 priority_scale = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerImage::maxDecodePriority(); | 1000 | if (!mInLocalCache) |
380 | U32 priority = (U32)(mPriority * priority_scale); | 1001 | { |
381 | return LLWorkerThread::PRIORITY_NORMAL | priority; | 1002 | mFetcher->mTextureCache->removeFromCache(mID); |
1003 | } | ||
382 | } | 1004 | } |
383 | 1005 | ||
384 | void LLTextureFetchWorker::setDesiredDiscard(S32 discard) | 1006 | |
1007 | ////////////////////////////////////////////////////////////////////////////// | ||
1008 | |||
1009 | bool LLTextureFetchWorker::processSimulatorPackets() | ||
385 | { | 1010 | { |
386 | if (mDesiredDiscard != discard) | 1011 | if (mLastPacket >= mFirstPacket) |
387 | { | 1012 | { |
388 | mDesiredDiscard = discard; | 1013 | S32 buffer_size = mFormattedImage->getDataSize(); |
389 | if (!haveWork()) | 1014 | for (S32 i = mFirstPacket; i<=mLastPacket; i++) |
1015 | { | ||
1016 | buffer_size += mPackets[i]->mSize; | ||
1017 | } | ||
1018 | bool have_all_data = mLastPacket >= mTotalPackets-1; | ||
1019 | llassert_always(mRequestedSize > 0); | ||
1020 | if (buffer_size >= mRequestedSize || have_all_data) | ||
390 | { | 1021 | { |
391 | addWork(0, calcWorkPriority()); | 1022 | /// We have enough (or all) data |
1023 | if (have_all_data) | ||
1024 | { | ||
1025 | mHaveAllData = TRUE; | ||
1026 | } | ||
1027 | S32 cur_size = mFormattedImage->getDataSize(); | ||
1028 | if (buffer_size > cur_size) | ||
1029 | { | ||
1030 | /// We have new data | ||
1031 | U8* buffer = new U8[buffer_size]; | ||
1032 | S32 offset = 0; | ||
1033 | if (cur_size > 0 && mFirstPacket > 0) | ||
1034 | { | ||
1035 | memcpy(buffer, mFormattedImage->getData(), cur_size); | ||
1036 | offset = cur_size; | ||
1037 | } | ||
1038 | for (S32 i=mFirstPacket; i<=mLastPacket; i++) | ||
1039 | { | ||
1040 | memcpy(buffer + offset, mPackets[i]->mData, mPackets[i]->mSize); | ||
1041 | offset += mPackets[i]->mSize; | ||
1042 | } | ||
1043 | // NOTE: setData releases current data | ||
1044 | mFormattedImage->setData(buffer, buffer_size); | ||
1045 | } | ||
1046 | mLoadedDiscard = mRequestedDiscard; | ||
1047 | return true; | ||
392 | } | 1048 | } |
393 | } | 1049 | } |
1050 | return false; | ||
394 | } | 1051 | } |
395 | 1052 | ||
396 | void LLTextureFetchWorker::setImagePriority(F32 priority) | 1053 | ////////////////////////////////////////////////////////////////////////////// |
1054 | |||
1055 | void LLTextureFetchWorker::callbackURLReceived(const LLSD& data, bool success) | ||
397 | { | 1056 | { |
398 | if (priority != mPriority) | 1057 | #if 0 |
1058 | LLMutexLock lock(&mWorkMutex); | ||
1059 | if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_URL) | ||
399 | { | 1060 | { |
400 | mPriority = priority; | 1061 | llwarns << "callbackURLReceived for unrequested fetch worker, req=" |
401 | setPriority(calcWorkPriority()); | 1062 | << mSentRequest << " state= " << mState << llendl; |
1063 | return; | ||
402 | } | 1064 | } |
1065 | if (success) | ||
1066 | { | ||
1067 | mURL = data.asString(); | ||
1068 | } | ||
1069 | mLoaded = TRUE; | ||
1070 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
1071 | #endif | ||
403 | } | 1072 | } |
404 | 1073 | ||
405 | void LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) | 1074 | ////////////////////////////////////////////////////////////////////////////// |
1075 | |||
1076 | void LLTextureFetchWorker::callbackHttpGet(U8* data, S32 data_size, bool last_block) | ||
406 | { | 1077 | { |
407 | PacketData* packet = new PacketData(data, size); | 1078 | #if 0 |
408 | 1079 | LLMutexLock lock(&mWorkMutex); | |
409 | lockWorkData(); | 1080 | if (!mSentRequest || mState != LOAD_FROM_HTTP_GET_DATA) |
410 | if (index >= (S32)mPackets.size()) | ||
411 | { | 1081 | { |
412 | mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers | 1082 | llwarns << "callbackHttpGet for unrequested fetch worker, req=" |
1083 | << mSentRequest << " state= " << mState << llendl; | ||
1084 | return; | ||
413 | } | 1085 | } |
414 | if (mPackets[index] != NULL) | 1086 | // llinfos << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << llendl; |
1087 | if (mLoaded) | ||
415 | { | 1088 | { |
416 | llwarns << "LLTextureFetchWorker::insertPacket called for duplicate packet: " << index << llendl; | 1089 | llwarns << "Duplicate callback for " << mID.asString() << llendl; |
1090 | return; // ignore duplicate callback | ||
417 | } | 1091 | } |
418 | mPackets[index] = packet; | 1092 | if (data_size >= 0) |
419 | while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL) | ||
420 | { | 1093 | { |
421 | ++mLastPacket; | 1094 | if (data_size > 0) |
1095 | { | ||
1096 | mBuffer = new U8[data_size]; | ||
1097 | // *TODO: set the formatted image data here | ||
1098 | memcpy(mBuffer, data, data_size); | ||
1099 | mBufferSize += data_size; | ||
1100 | if (data_size < mRequestedSize || last_block == true) | ||
1101 | { | ||
1102 | mHaveAllData = TRUE; | ||
1103 | } | ||
1104 | else if (data_size > mRequestedSize) | ||
1105 | { | ||
1106 | // *TODO: This will happen until we fix LLCurl::getByteRange() | ||
1107 | llinfos << "HUH?" << llendl; | ||
1108 | mHaveAllData = TRUE; | ||
1109 | mFormattedImage->deleteData(); | ||
1110 | mBufferSize = data_size; | ||
1111 | } | ||
1112 | } | ||
1113 | else | ||
1114 | { | ||
1115 | // We requested data but received none (and no error), | ||
1116 | // so presumably we have all of it | ||
1117 | mHaveAllData = TRUE; | ||
1118 | } | ||
1119 | mRequestedSize = data_size; | ||
422 | } | 1120 | } |
423 | unlockWorkData(); | 1121 | else |
1122 | { | ||
1123 | mRequestedSize = -1; // error | ||
1124 | } | ||
1125 | mLoaded = TRUE; | ||
1126 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
1127 | #endif | ||
424 | } | 1128 | } |
425 | 1129 | ||
426 | // Called from LLWorkerThread::processRequest() | 1130 | ////////////////////////////////////////////////////////////////////////////// |
427 | bool LLTextureFetchWorker::doWork(S32 param) | 1131 | |
1132 | void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, | ||
1133 | S32 imagesize, BOOL islocal) | ||
428 | { | 1134 | { |
429 | switch(mState) | 1135 | LLMutexLock lock(&mWorkMutex); |
430 | { | 1136 | if (mState != LOAD_FROM_TEXTURE_CACHE) |
431 | case INIT: | 1137 | { |
432 | { | 1138 | llwarns << "Read callback for " << mID << " with state = " << mState << llendl; |
433 | // fall through | 1139 | return; |
434 | } | ||
435 | case LOAD_FROM_CACHE: | ||
436 | { | ||
437 | // Load any existing data from the cache | ||
438 | if (mFileHandle == LLVFSThread::nullHandle()) | ||
439 | { | ||
440 | bool res = startVFSLoad(gVFS, LLAssetType::AT_TEXTURE); | ||
441 | if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE_TGA); | ||
442 | if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE); | ||
443 | if (!res) | ||
444 | { | ||
445 | // Didn't load from VFS | ||
446 | mFormattedImage = new LLImageJ2C; | ||
447 | mState = LOAD_FROM_SIMULATOR; | ||
448 | } | ||
449 | } | ||
450 | if (mFileHandle != LLVFSThread::nullHandle()) | ||
451 | { | ||
452 | if (!loadFromVFS()) | ||
453 | { | ||
454 | return false; // not done | ||
455 | } | ||
456 | if (!mLoaded) | ||
457 | { | ||
458 | llwarns << "Load from VFS failed on: " << mID << llendl; | ||
459 | return true; | ||
460 | } | ||
461 | bool res = mFormattedImage->setData(mBuffer, mBufferSize); | ||
462 | if (!res) | ||
463 | { | ||
464 | llwarns << "loadLocalImage() - setData() failed" << llendl; | ||
465 | mFormattedImage->deleteData(); | ||
466 | return true; | ||
467 | } | ||
468 | // Successfully loaded | ||
469 | if (mFormattedImage->getDiscardLevel() <= mRequestedDiscard) | ||
470 | { | ||
471 | // we have enough data, decode it | ||
472 | mState = DECODE_IMAGE; | ||
473 | mRequestedSize = mBufferSize; | ||
474 | } | ||
475 | else | ||
476 | { | ||
477 | // need more data | ||
478 | mState = LOAD_FROM_SIMULATOR; | ||
479 | mRequestedSize = mFormattedImage->calcDataSize(mRequestedDiscard); | ||
480 | } | ||
481 | } | ||
482 | return false; | ||
483 | } | ||
484 | case LOAD_FROM_SIMULATOR: | ||
485 | { | ||
486 | if (!mRequested) | ||
487 | { | ||
488 | lockQueue(); | ||
489 | sNetworkQueue.insert(this); | ||
490 | unlockQueue(); | ||
491 | mRequested = TRUE; | ||
492 | } | ||
493 | if (processSimulatorPackets()) | ||
494 | { | ||
495 | mState = WRITE_TO_VFS; | ||
496 | } | ||
497 | return false; | ||
498 | } | ||
499 | case WRITE_TO_VFS: | ||
500 | { | ||
501 | mState = DECODE_IMAGE; | ||
502 | // fall through | ||
503 | } | ||
504 | case DECODE_IMAGE: | ||
505 | { | ||
506 | startDecode(); | ||
507 | mState = DECODE_IMAGE_UPDATE; | ||
508 | // fall through | ||
509 | } | ||
510 | case DECODE_IMAGE_UPDATE: | ||
511 | { | ||
512 | if (decodeImage()) | ||
513 | { | ||
514 | mState = DONE; | ||
515 | } | ||
516 | return false; | ||
517 | } | ||
518 | case DONE: | ||
519 | { | ||
520 | // Do any cleanup here | ||
521 | // Destroy the formatted image, we don't need it any more (raw image is still valid) | ||
522 | mFormattedImage = NULL; | ||
523 | mIdleTimer.reset(); | ||
524 | return true; | ||
525 | } | ||
526 | default: | ||
527 | { | ||
528 | llerrs << "LLTextureFetchWorker::doWork() has illegal state" << llendl; | ||
529 | return true; | ||
530 | } | ||
531 | } | 1140 | } |
1141 | if (success) | ||
1142 | { | ||
1143 | llassert_always(imagesize > 0); | ||
1144 | mFileSize = imagesize; | ||
1145 | mFormattedImage = image; | ||
1146 | mImageCodec = image->getCodec(); | ||
1147 | mInLocalCache = islocal; | ||
1148 | if (mFileSize != 0 && mFormattedImage->getDataSize() >= mFileSize) | ||
1149 | { | ||
1150 | mHaveAllData = TRUE; | ||
1151 | } | ||
1152 | } | ||
1153 | mLoaded = TRUE; | ||
1154 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
532 | } | 1155 | } |
533 | 1156 | ||
534 | // Called from MAIN thread | 1157 | void LLTextureFetchWorker::callbackCacheWrite(bool success) |
535 | void LLTextureFetchWorker::startWork(S32 param) | ||
536 | { | 1158 | { |
1159 | LLMutexLock lock(&mWorkMutex); | ||
1160 | if (mState != WAIT_ON_WRITE) | ||
1161 | { | ||
1162 | llwarns << "Write callback for " << mID << " with state = " << mState << llendl; | ||
1163 | return; | ||
1164 | } | ||
1165 | mWritten = TRUE; | ||
1166 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
537 | } | 1167 | } |
538 | 1168 | ||
539 | void LLTextureFetchWorker::endWork(S32 param, bool aborted) | 1169 | ////////////////////////////////////////////////////////////////////////////// |
1170 | |||
1171 | void LLTextureFetchWorker::callbackDecoded(bool success) | ||
540 | { | 1172 | { |
1173 | if (mState != DECODE_IMAGE_UPDATE) | ||
1174 | { | ||
1175 | llwarns << "Decode callback for " << mID << " with state = " << mState << llendl; | ||
1176 | return; | ||
1177 | } | ||
1178 | // llinfos << mID << " : DECODE COMPLETE " << llendl; | ||
1179 | setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); | ||
541 | } | 1180 | } |
542 | 1181 | ||
543 | ////////////////////////////////////////////////////////////////////////////// | 1182 | ////////////////////////////////////////////////////////////////////////////// |
544 | 1183 | ||
545 | bool LLTextureFetchWorker::startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type) | 1184 | bool LLTextureFetchWorker::decodeImage() |
546 | { | 1185 | { |
547 | // Start load from VFS if it's there | 1186 | llassert_always(mImageWorker); |
548 | if (vfs->getExists(mID, asset_type)) | 1187 | bool res = true; |
1188 | if (mRawImage.isNull()) | ||
549 | { | 1189 | { |
550 | mBufferSize = vfs->getSize(mID, asset_type); | 1190 | res = false; |
551 | mBuffer = new U8[mBufferSize]; | 1191 | if (mImageWorker->requestDecodedData(mRawImage, -1)) |
552 | mFileHandle = LLVFSThread::sLocal->read(vfs, mID, asset_type, mBuffer, 0, mBufferSize); | ||
553 | if (mFileHandle == LLVFSThread::nullHandle()) | ||
554 | { | 1192 | { |
555 | llwarns << "loadLocalImage() - vfs read failed in static VFS: " << mID << llendl; | 1193 | res = true; |
556 | delete mBuffer; | 1194 | // llinfos << mID << " : BASE DECODE FINISHED" << llendl; |
557 | mBuffer = NULL; | ||
558 | return false; | ||
559 | } | 1195 | } |
560 | if (asset_type == LLAssetType::AT_TEXTURE_TGA) | 1196 | } |
1197 | if (res && | ||
1198 | (mRawImage.notNull() && mRawImage->getDataSize() > 0) && | ||
1199 | (mNeedsAux && mAuxImage.isNull())) | ||
1200 | { | ||
1201 | res = false; | ||
1202 | if (mImageWorker->requestDecodedAuxData(mAuxImage, 4, -1)) | ||
561 | { | 1203 | { |
562 | mFormattedImage = new LLImageTGA; | 1204 | res = true; |
1205 | // llinfos << mID << " : AUX DECODE FINISHED" << llendl; | ||
563 | } | 1206 | } |
564 | else if (asset_type == LLAssetType::AT_TEXTURE) | 1207 | } |
1208 | if (res) | ||
1209 | { | ||
1210 | if ((mRawImage.notNull() && mRawImage->getDataSize() > 0) && | ||
1211 | (!mNeedsAux || (mAuxImage.notNull() && mAuxImage->getDataSize() > 0))) | ||
565 | { | 1212 | { |
566 | mFormattedImage = new LLImageJ2C; | 1213 | mDecodedDiscard = mFormattedImage->getDiscardLevel(); |
1214 | // llinfos << mID << " : DECODE FINISHED. DISCARD: " << mDecodedDiscard << llendl; | ||
567 | } | 1215 | } |
568 | else | 1216 | else |
569 | { | 1217 | { |
570 | llerrs << "LLTextureFetchWorker::startVFSLoad called with bad asset type: " << asset_type << llendl; | 1218 | llwarns << "DECODE FAILED: " << mID << " Discard: " << (S32)mFormattedImage->getDiscardLevel() << llendl; |
1219 | removeFromCache(); | ||
571 | } | 1220 | } |
572 | return true; | 1221 | mImageWorker->scheduleDelete(); |
1222 | mImageWorker = NULL; | ||
573 | } | 1223 | } |
574 | return false; | 1224 | return res; |
575 | } | 1225 | } |
576 | 1226 | ||
577 | bool LLTextureFetchWorker::loadFromVFS() | 1227 | ////////////////////////////////////////////////////////////////////////////// |
1228 | |||
1229 | bool LLTextureFetchWorker::writeToCacheComplete() | ||
578 | { | 1230 | { |
579 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | 1231 | // Complete write to cache |
1232 | if (mCacheWriteHandle != LLTextureCache::nullHandle()) | ||
1233 | { | ||
1234 | if (!mWritten) | ||
1235 | { | ||
1236 | return false; | ||
1237 | } | ||
1238 | if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) | ||
1239 | { | ||
1240 | mCacheWriteHandle = LLTextureCache::nullHandle(); | ||
1241 | } | ||
1242 | else | ||
1243 | { | ||
1244 | return false; | ||
1245 | } | ||
1246 | } | ||
1247 | return true; | ||
1248 | } | ||
580 | 1249 | ||
581 | llassert(mLoaded == FALSE); | 1250 | |
582 | 1251 | ////////////////////////////////////////////////////////////////////////////// | |
583 | // Check loading status | 1252 | ////////////////////////////////////////////////////////////////////////////// |
584 | LLVFSThread::status_t status = LLVFSThread::sLocal->getRequestStatus(mFileHandle); | 1253 | // public |
585 | if (status == LLVFSThread::STATUS_QUEUED || status == LLVFSThread::STATUS_INPROGRESS) | 1254 | |
1255 | LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded) | ||
1256 | : LLWorkerThread("TextureFetch", threaded), | ||
1257 | mDebugCount(0), | ||
1258 | mDebugPause(FALSE), | ||
1259 | mQueueMutex(getAPRPool()), | ||
1260 | mTextureCache(cache) | ||
1261 | { | ||
1262 | } | ||
1263 | |||
1264 | LLTextureFetch::~LLTextureFetch() | ||
1265 | { | ||
1266 | // ~LLQueuedThread() called here | ||
1267 | } | ||
1268 | |||
1269 | bool LLTextureFetch::createRequest(const LLUUID& id, const LLHost& host, F32 priority, | ||
1270 | S32 w, S32 h, S32 c, S32 discard, bool needs_aux) | ||
1271 | { | ||
1272 | if (mDebugPause) | ||
586 | { | 1273 | { |
587 | return false; | 1274 | return false; |
588 | } | 1275 | } |
589 | else if (status == LLVFSThread::STATUS_COMPLETE) | 1276 | |
1277 | LLTextureFetchWorker* worker = NULL; | ||
1278 | LLMutexLock lock(&mQueueMutex); | ||
1279 | map_t::iterator iter = mRequestMap.find(id); | ||
1280 | if (iter != mRequestMap.end()) | ||
590 | { | 1281 | { |
591 | mLoaded = TRUE; | 1282 | worker = iter->second; |
592 | return true; | 1283 | if (worker->mHost != host) |
1284 | { | ||
1285 | llwarns << "LLTextureFetch::createRequest " << id << " called with multiple hosts: " | ||
1286 | << host << " != " << worker->mHost << llendl; | ||
1287 | removeRequest(worker, true); | ||
1288 | worker = NULL; | ||
1289 | return false; | ||
1290 | } | ||
1291 | } | ||
1292 | // If the requester knows the dimentions of the image, | ||
1293 | // this will calculate how much data we need without having to parse the header | ||
1294 | S32 desired_size; | ||
1295 | if (w*h*c > 0) | ||
1296 | { | ||
1297 | desired_size = LLImageJ2C::calcDataSizeJ2C(w, h, c, discard); | ||
593 | } | 1298 | } |
594 | else | 1299 | else |
595 | { | 1300 | { |
596 | llwarns << "loadLocalImage() - vfs read failed" << llendl; | 1301 | desired_size = FIRST_PACKET_SIZE; |
597 | LLVFSThread::Request* req = (LLVFSThread::Request*)LLVFSThread::sLocal->getRequest(mFileHandle); | 1302 | discard = MAX_DISCARD_LEVEL; |
598 | if (req && mFormattedImage.notNull()) | 1303 | } |
1304 | if (worker) | ||
1305 | { | ||
1306 | if (worker->wasAborted()) | ||
599 | { | 1307 | { |
600 | LLVFS* vfs = req->getVFS(); | 1308 | return false; // need to wait for previous aborted request to complete |
601 | LLAssetType::EType asset_type = mFormattedImage->getCodec() == IMG_CODEC_TGA ? LLAssetType::AT_TEXTURE_TGA : LLAssetType::AT_TEXTURE; | 1309 | } |
602 | vfs->removeFile(mID, asset_type); | 1310 | worker->lockWorkData(); |
1311 | worker->setImagePriority(priority); | ||
1312 | worker->setDesiredDiscard(discard, desired_size); | ||
1313 | worker->unlockWorkData(); | ||
1314 | if (!worker->haveWork()) | ||
1315 | { | ||
1316 | worker->mState = LLTextureFetchWorker::INIT; | ||
1317 | worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); | ||
603 | } | 1318 | } |
604 | return true; | ||
605 | } | 1319 | } |
1320 | else | ||
1321 | { | ||
1322 | worker = new LLTextureFetchWorker(this, id, host, priority, discard, desired_size); | ||
1323 | mRequestMap[id] = worker; | ||
1324 | } | ||
1325 | worker->mActiveCount++; | ||
1326 | worker->mNeedsAux = needs_aux; | ||
1327 | // llinfos << "REQUESTED: " << id << " Discard: " << discard << llendl; | ||
1328 | return true; | ||
606 | } | 1329 | } |
607 | 1330 | ||
1331 | void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) | ||
1332 | { | ||
1333 | LLMutexLock lock(&mQueueMutex); | ||
1334 | LLTextureFetchWorker* worker = getWorker(id); | ||
1335 | if (worker) | ||
1336 | { | ||
1337 | removeRequest(worker, cancel); | ||
1338 | } | ||
1339 | } | ||
608 | 1340 | ||
609 | ////////////////////////////////////////////////////////////////////////////// | 1341 | // protected |
610 | 1342 | ||
611 | bool LLTextureFetchWorker::processSimulatorPackets() | 1343 | // call lockQueue() first! |
1344 | void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) | ||
1345 | { | ||
1346 | if (mRequestMap.find(worker->mID) != mRequestMap.end()) | ||
1347 | { | ||
1348 | // only add to the queue if in the request map | ||
1349 | // i.e. a delete has not been requested | ||
1350 | mNetworkQueue.insert(worker->mID); | ||
1351 | } | ||
1352 | for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); | ||
1353 | iter1 != mCancelQueue.end(); ++iter1) | ||
1354 | { | ||
1355 | iter1->second.erase(worker->mID); | ||
1356 | } | ||
1357 | } | ||
1358 | |||
1359 | // call lockQueue() first! | ||
1360 | void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker) | ||
1361 | { | ||
1362 | mNetworkQueue.erase(worker->mID); | ||
1363 | } | ||
1364 | |||
1365 | // call lockQueue() first! | ||
1366 | void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) | ||
1367 | { | ||
1368 | mRequestMap.erase(worker->mID); | ||
1369 | size_t erased = mNetworkQueue.erase(worker->mID); | ||
1370 | if (cancel && erased > 0) | ||
1371 | { | ||
1372 | mCancelQueue[worker->mHost].insert(worker->mID); | ||
1373 | } | ||
1374 | worker->scheduleDelete(); | ||
1375 | } | ||
1376 | |||
1377 | // call lockQueue() first! | ||
1378 | LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) | ||
1379 | { | ||
1380 | LLTextureFetchWorker* res = NULL; | ||
1381 | map_t::iterator iter = mRequestMap.find(id); | ||
1382 | if (iter != mRequestMap.end()) | ||
1383 | { | ||
1384 | res = iter->second; | ||
1385 | } | ||
1386 | return res; | ||
1387 | } | ||
1388 | |||
1389 | |||
1390 | bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, | ||
1391 | LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux) | ||
612 | { | 1392 | { |
613 | bool res = false; | 1393 | bool res = false; |
614 | lockWorkData(); | 1394 | LLMutexLock lock(&mQueueMutex); |
615 | if (mLastPacket >= 0) | 1395 | LLTextureFetchWorker* worker = getWorker(id); |
1396 | if (worker) | ||
616 | { | 1397 | { |
617 | S32 data_size = 0; | 1398 | if (worker->wasAborted()) |
618 | for (S32 i = 0; i<=mLastPacket; i++) | ||
619 | { | 1399 | { |
620 | data_size += mPackets[i]->mSize; | 1400 | res = true; |
621 | } | 1401 | } |
622 | if (data_size >= mRequestedSize || mLastPacket == mTotalPackets) | 1402 | else if (!worker->haveWork()) |
623 | { | 1403 | { |
624 | /// We have enough (or all) data, copy it into mBuffer | 1404 | // Should only happen if we set mDebugPause... |
625 | if (mBufferSize < data_size) | 1405 | if (!mDebugPause) |
626 | { | 1406 | { |
627 | delete mBuffer; | 1407 | // llwarns << "Adding work for inactive worker: " << id << llendl; |
628 | mBuffer = new U8[data_size]; | 1408 | worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); |
629 | mBufferSize = data_size; | ||
630 | } | 1409 | } |
631 | S32 offset = 0; | 1410 | } |
632 | for (S32 i = 0; i<=mLastPacket; i++) | 1411 | else if (worker->checkWork()) |
1412 | { | ||
1413 | discard_level = worker->mDecodedDiscard; | ||
1414 | raw = worker->mRawImage; worker->mRawImage = NULL; | ||
1415 | aux = worker->mAuxImage; worker->mAuxImage = NULL; | ||
1416 | res = true; | ||
1417 | } | ||
1418 | else | ||
1419 | { | ||
1420 | worker->lockWorkData(); | ||
1421 | if ((worker->mDecodedDiscard >= 0) && | ||
1422 | (worker->mDecodedDiscard < discard_level || discard_level < 0) && | ||
1423 | (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) | ||
633 | { | 1424 | { |
634 | memcpy(mBuffer + offset, mPackets[i]->mData, mPackets[i]->mSize); | 1425 | // Not finished, but data is ready |
635 | offset += mPackets[i]->mSize; | 1426 | discard_level = worker->mDecodedDiscard; |
1427 | if (worker->mRawImage) raw = worker->mRawImage; | ||
1428 | if (worker->mAuxImage) aux = worker->mAuxImage; | ||
636 | } | 1429 | } |
637 | res = true; | 1430 | worker->unlockWorkData(); |
638 | } | 1431 | } |
639 | } | 1432 | } |
640 | unlockWorkData(); | 1433 | else |
1434 | { | ||
1435 | res = true; | ||
1436 | } | ||
1437 | return res; | ||
1438 | } | ||
1439 | |||
1440 | bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) | ||
1441 | { | ||
1442 | bool res = false; | ||
1443 | LLMutexLock lock(&mQueueMutex); | ||
1444 | LLTextureFetchWorker* worker = getWorker(id); | ||
1445 | if (worker) | ||
1446 | { | ||
1447 | worker->lockWorkData(); | ||
1448 | worker->setImagePriority(priority); | ||
1449 | worker->unlockWorkData(); | ||
1450 | res = true; | ||
1451 | } | ||
641 | return res; | 1452 | return res; |
642 | } | 1453 | } |
643 | 1454 | ||
644 | ////////////////////////////////////////////////////////////////////////////// | 1455 | ////////////////////////////////////////////////////////////////////////////// |
645 | 1456 | ||
646 | void LLTextureFetchWorker::startDecode() | 1457 | //virtual |
1458 | S32 LLTextureFetch::update(U32 max_time_ms) | ||
647 | { | 1459 | { |
648 | mRawImage = NULL; | 1460 | S32 res; |
649 | mAuxImage = NULL; | 1461 | res = LLWorkerThread::update(max_time_ms); |
1462 | |||
1463 | const F32 REQUEST_TIME = 1.f; | ||
1464 | |||
1465 | // Periodically, gather the list of textures that need data from the network | ||
1466 | // And send the requests out to the simulators | ||
1467 | if (mNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME) | ||
1468 | { | ||
1469 | mNetworkTimer.reset(); | ||
1470 | sendRequestListToSimulators(); | ||
1471 | } | ||
1472 | |||
1473 | return res; | ||
650 | } | 1474 | } |
651 | 1475 | ||
652 | bool LLTextureFetchWorker::decodeImage() | 1476 | ////////////////////////////////////////////////////////////////////////////// |
1477 | |||
1478 | void LLTextureFetch::sendRequestListToSimulators() | ||
653 | { | 1479 | { |
654 | const F32 MAX_DECODE_TIME = .001f; // 1 ms | 1480 | const S32 IMAGES_PER_REQUEST = 50; |
655 | if (mRawImage->getDataSize() == 0) | 1481 | const F32 LAZY_FLUSH_TIMEOUT = 15.f; // 10.0f // temp |
1482 | const F32 MIN_REQUEST_TIME = 1.0f; | ||
1483 | const F32 MIN_DELTA_PRIORITY = 1000.f; | ||
1484 | |||
1485 | LLMutexLock lock(&mQueueMutex); | ||
1486 | |||
1487 | // Send requests | ||
1488 | typedef std::set<LLTextureFetchWorker*,LLTextureFetchWorker::Compare> request_list_t; | ||
1489 | typedef std::map< LLHost, request_list_t > work_request_map_t; | ||
1490 | work_request_map_t requests; | ||
1491 | for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) | ||
656 | { | 1492 | { |
657 | if (!mFormattedImage->requestDecodedData(mRawImage, -1, MAX_DECODE_TIME)) | 1493 | queue_t::iterator curiter = iter++; |
1494 | LLTextureFetchWorker* req = getWorker(*curiter); | ||
1495 | if (!req) | ||
658 | { | 1496 | { |
659 | return false; | 1497 | mNetworkQueue.erase(curiter); |
1498 | continue; // paranoia | ||
1499 | } | ||
1500 | if (req->mID == mDebugID) | ||
1501 | { | ||
1502 | mDebugCount++; // for setting breakpoints | ||
1503 | } | ||
1504 | if (req->mTotalPackets > 0 && req->mLastPacket >= req->mTotalPackets-1) | ||
1505 | { | ||
1506 | // We have all the packets... make sure this is high priority | ||
1507 | // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); | ||
1508 | continue; | ||
1509 | } | ||
1510 | F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); | ||
1511 | F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); | ||
1512 | if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || | ||
1513 | (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || | ||
1514 | (elapsed >= LAZY_FLUSH_TIMEOUT)) | ||
1515 | { | ||
1516 | requests[req->mHost].insert(req); | ||
660 | } | 1517 | } |
661 | mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image | ||
662 | } | 1518 | } |
663 | if (mNeedsAux && mAuxImage->getDataSize() == 0) | 1519 | |
1520 | std::string http_url; | ||
1521 | #if 0 | ||
1522 | if (gSavedSettings.getBOOL("ImagePipelineUseHTTP")) | ||
664 | { | 1523 | { |
665 | if (!mFormattedImage->requestDecodedAuxData(mAuxImage, 4, -1, MAX_DECODE_TIME )) | 1524 | LLViewerRegion* region = gAgent.getRegion(); |
1525 | if (region) | ||
666 | { | 1526 | { |
667 | return false; | 1527 | http_url = region->getCapability("RequestTextureDownload"); |
668 | } | 1528 | } |
669 | mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image | ||
670 | } | 1529 | } |
671 | mDecodedDiscard = mFormattedImage->getDiscardLevel(); | 1530 | #endif |
672 | return true; | 1531 | |
1532 | for (work_request_map_t::iterator iter1 = requests.begin(); | ||
1533 | iter1 != requests.end(); ++iter1) | ||
1534 | { | ||
1535 | bool use_http = http_url.empty() ? false : true; | ||
1536 | LLHost host = iter1->first; | ||
1537 | // invalid host = use agent host | ||
1538 | if (host == LLHost::invalid) | ||
1539 | { | ||
1540 | host = gAgent.getRegionHost(); | ||
1541 | } | ||
1542 | else | ||
1543 | { | ||
1544 | use_http = false; | ||
1545 | } | ||
1546 | |||
1547 | if (use_http) | ||
1548 | { | ||
1549 | } | ||
1550 | else | ||
1551 | { | ||
1552 | S32 request_count = 0; | ||
1553 | for (request_list_t::iterator iter2 = iter1->second.begin(); | ||
1554 | iter2 != iter1->second.end(); ++iter2) | ||
1555 | { | ||
1556 | LLTextureFetchWorker* req = *iter2; | ||
1557 | req->mSentRequest = LLTextureFetchWorker::SENT_SIM; | ||
1558 | if (0 == request_count) | ||
1559 | { | ||
1560 | gMessageSystem->newMessageFast(_PREHASH_RequestImage); | ||
1561 | gMessageSystem->nextBlockFast(_PREHASH_AgentData); | ||
1562 | gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
1563 | gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
1564 | } | ||
1565 | S32 packet = req->mLastPacket + 1; | ||
1566 | gMessageSystem->nextBlockFast(_PREHASH_RequestImage); | ||
1567 | gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID); | ||
1568 | gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, (S8)req->mSimRequestedDiscard); | ||
1569 | gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mImagePriority); | ||
1570 | gMessageSystem->addU32Fast(_PREHASH_Packet, packet); | ||
1571 | gMessageSystem->addU8Fast(_PREHASH_Type, req->mType); | ||
1572 | // llinfos << "IMAGE REQUEST: " << req->mID << " Discard: " << req->mDesiredDiscard | ||
1573 | // << " Packet: " << packet << " Priority: " << req->mImagePriority << llendl; | ||
1574 | |||
1575 | req->lockWorkData(); | ||
1576 | req->mSimRequestedDiscard = req->mDesiredDiscard; | ||
1577 | req->mRequestedPriority = req->mImagePriority; | ||
1578 | req->mRequestedTimer.reset(); | ||
1579 | req->unlockWorkData(); | ||
1580 | request_count++; | ||
1581 | if (request_count >= IMAGES_PER_REQUEST) | ||
1582 | { | ||
1583 | // llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; | ||
1584 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | ||
1585 | request_count = 0; | ||
1586 | } | ||
1587 | } | ||
1588 | if (request_count > 0 && request_count < IMAGES_PER_REQUEST) | ||
1589 | { | ||
1590 | // llinfos << "REQUESTING " << request_count << " IMAGES FROM HOST: " << host.getIPString() << llendl; | ||
1591 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | ||
1592 | request_count = 0; | ||
1593 | } | ||
1594 | } | ||
1595 | } | ||
1596 | |||
1597 | // Send cancelations | ||
1598 | if (!mCancelQueue.empty()) | ||
1599 | { | ||
1600 | for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); | ||
1601 | iter1 != mCancelQueue.end(); ++iter1) | ||
1602 | { | ||
1603 | LLHost host = iter1->first; | ||
1604 | if (host == LLHost::invalid) | ||
1605 | { | ||
1606 | host = gAgent.getRegionHost(); | ||
1607 | } | ||
1608 | S32 request_count = 0; | ||
1609 | for (queue_t::iterator iter2 = iter1->second.begin(); | ||
1610 | iter2 != iter1->second.end(); ++iter2) | ||
1611 | { | ||
1612 | if (0 == request_count) | ||
1613 | { | ||
1614 | gMessageSystem->newMessageFast(_PREHASH_RequestImage); | ||
1615 | gMessageSystem->nextBlockFast(_PREHASH_AgentData); | ||
1616 | gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
1617 | gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
1618 | } | ||
1619 | gMessageSystem->nextBlockFast(_PREHASH_RequestImage); | ||
1620 | gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); | ||
1621 | gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); | ||
1622 | gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); | ||
1623 | gMessageSystem->addU32Fast(_PREHASH_Packet, 0); | ||
1624 | gMessageSystem->addU8Fast(_PREHASH_Type, 0); | ||
1625 | // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; | ||
1626 | |||
1627 | request_count++; | ||
1628 | if (request_count >= IMAGES_PER_REQUEST) | ||
1629 | { | ||
1630 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | ||
1631 | request_count = 0; | ||
1632 | } | ||
1633 | } | ||
1634 | if (request_count > 0 && request_count < IMAGES_PER_REQUEST) | ||
1635 | { | ||
1636 | gMessageSystem->sendSemiReliable(host, NULL, NULL); | ||
1637 | } | ||
1638 | } | ||
1639 | mCancelQueue.clear(); | ||
1640 | } | ||
673 | } | 1641 | } |
674 | 1642 | ||
675 | ////////////////////////////////////////////////////////////////////////////// | 1643 | ////////////////////////////////////////////////////////////////////////////// |
676 | 1644 | ||
677 | #if 0 | 1645 | bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) |
678 | // static | ||
679 | void LLTextureFetchWorker::receiveImageHeader(LLMessageSystem *msg, void **user_data) | ||
680 | { | 1646 | { |
681 | LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); | 1647 | mRequestedTimer.reset(); |
1648 | if (index >= mTotalPackets) | ||
1649 | { | ||
1650 | // llwarns << "Received Image Packet " << index << " > max: " << mTotalPackets << " for image: " << mID << llendl; | ||
1651 | return false; | ||
1652 | } | ||
1653 | if (index > 0 && index < mTotalPackets-1 && size != MAX_IMG_PACKET_SIZE) | ||
1654 | { | ||
1655 | // llwarns << "Received bad sized packet: " << index << ", " << size << " != " << MAX_IMG_PACKET_SIZE << " for image: " << mID << llendl; | ||
1656 | return false; | ||
1657 | } | ||
682 | 1658 | ||
683 | // Receive image header, copy into image object and decompresses | 1659 | if (index >= (S32)mPackets.size()) |
684 | // if this is a one-packet image. | 1660 | { |
1661 | mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers | ||
1662 | } | ||
1663 | else if (mPackets[index] != NULL) | ||
1664 | { | ||
1665 | // llwarns << "Received duplicate packet: " << index << " for image: " << mID << llendl; | ||
1666 | return false; | ||
1667 | } | ||
1668 | |||
1669 | mPackets[index] = new PacketData(data, size); | ||
1670 | while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL) | ||
1671 | { | ||
1672 | ++mLastPacket; | ||
1673 | } | ||
1674 | return true; | ||
1675 | } | ||
685 | 1676 | ||
686 | gImageList.sTextureBits += msg->getReceiveBytes(); | 1677 | bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, |
687 | gImageList.sTexturePackets++; | 1678 | U16 data_size, U8* data) |
1679 | { | ||
1680 | LLMutexLock lock(&mQueueMutex); | ||
1681 | LLTextureFetchWorker* worker = getWorker(id); | ||
1682 | bool res = true; | ||
688 | 1683 | ||
689 | LLUUID id; | 1684 | ++mPacketCount; |
690 | msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); | ||
691 | // LLString ip_string(u32_to_ip_string(msg->getSenderIP())); | ||
692 | 1685 | ||
693 | LLTextureFetchWorker* worker = getActiveWorker(id); | ||
694 | if (!worker) | 1686 | if (!worker) |
695 | { | 1687 | { |
696 | llwarns << "receiveImageHeader for non active worker: " << id << llendl; | 1688 | // llwarns << "Received header for non active worker: " << id << llendl; |
697 | return; | 1689 | res = false; |
698 | } | 1690 | } |
699 | worker->mRequestedTimer.reset(); | 1691 | else if (worker->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK || |
700 | 1692 | worker->mSentRequest != LLTextureFetchWorker::SENT_SIM) | |
701 | // check to see if we've gotten this packet before | ||
702 | if (worker->mLastPacket != -1) | ||
703 | { | 1693 | { |
704 | llwarns << "Img: " << id << ":" << " Duplicate Image Header" << llendl; | 1694 | // llwarns << "receiveImageHeader for worker: " << id |
705 | return; | 1695 | // << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] |
1696 | // << " sent: " << worker->mSentRequest << llendl; | ||
1697 | res = false; | ||
706 | } | 1698 | } |
707 | 1699 | else if (worker->mLastPacket != -1) | |
708 | // Copy header data into image object | ||
709 | worker->lockWorkData(); | ||
710 | msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, image->mDataCodec); | ||
711 | msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, image->mTotalPackets); | ||
712 | msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, image->mTotalBytes); | ||
713 | if (0 == image->mTotalPackets) | ||
714 | { | 1700 | { |
715 | llwarns << "Img: " << id << ":" << " Number of packets is 0" << llendl; | 1701 | // check to see if we've gotten this packet before |
1702 | // llwarns << "Received duplicate header for: " << id << llendl; | ||
1703 | res = false; | ||
716 | } | 1704 | } |
717 | worker->unlockWorkData(); | 1705 | else if (!data_size) |
718 | 1706 | { | |
719 | U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); | 1707 | llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl; |
720 | if (data_size) | 1708 | res = false; |
1709 | } | ||
1710 | if (!res) | ||
721 | { | 1711 | { |
722 | U8 *data = new U8[data_size]; | 1712 | ++mBadPacketCount; |
723 | msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); | 1713 | mCancelQueue[host].insert(id); |
724 | worker->insertPacket(0, data, data_size) | 1714 | return false; |
725 | } | 1715 | } |
1716 | |||
1717 | worker->lockWorkData(); | ||
1718 | |||
1719 | // Copy header data into image object | ||
1720 | worker->mImageCodec = codec; | ||
1721 | worker->mTotalPackets = packets; | ||
1722 | worker->mFileSize = (S32)totalbytes; | ||
1723 | llassert_always(totalbytes > 0); | ||
1724 | llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); | ||
1725 | res = worker->insertPacket(0, data, data_size); | ||
1726 | worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); | ||
1727 | worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; | ||
1728 | worker->unlockWorkData(); | ||
1729 | return res; | ||
726 | } | 1730 | } |
727 | 1731 | ||
728 | /////////////////////////////////////////////////////////////////////////////// | 1732 | bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) |
729 | // static | ||
730 | void LLTextureFetchWorker::receiveImagePacket(LLMessageSystem *msg, void **user_data) | ||
731 | { | 1733 | { |
732 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | 1734 | LLMutexLock lock(&mQueueMutex); |
733 | LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); | 1735 | LLTextureFetchWorker* worker = getWorker(id); |
1736 | bool res = true; | ||
1737 | |||
1738 | ++mPacketCount; | ||
734 | 1739 | ||
735 | gImageList.sTextureBits += msg->getReceiveBytes(); | 1740 | if (!worker) |
736 | gImageList.sTexturePackets++; | 1741 | { |
1742 | // llwarns << "Received packet " << packet_num << " for non active worker: " << id << llendl; | ||
1743 | res = false; | ||
1744 | } | ||
1745 | else if (worker->mLastPacket == -1) | ||
1746 | { | ||
1747 | // llwarns << "Received packet " << packet_num << " before header for: " << id << llendl; | ||
1748 | res = false; | ||
1749 | } | ||
1750 | else if (!data_size) | ||
1751 | { | ||
1752 | llwarns << "Img: " << id << ":" << " Empty Image Header" << llendl; | ||
1753 | res = false; | ||
1754 | } | ||
1755 | if (!res) | ||
1756 | { | ||
1757 | ++mBadPacketCount; | ||
1758 | mCancelQueue[host].insert(id); | ||
1759 | return false; | ||
1760 | } | ||
737 | 1761 | ||
738 | LLUUID id; | 1762 | worker->lockWorkData(); |
739 | msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); | 1763 | |
740 | // LLString ip_string(u32_to_ip_string(msg->getSenderIP())); | 1764 | res = worker->insertPacket(packet_num, data, data_size); |
1765 | |||
1766 | if ((worker->mState == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) || | ||
1767 | (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) | ||
1768 | { | ||
1769 | worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); | ||
1770 | worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; | ||
1771 | } | ||
1772 | else | ||
1773 | { | ||
1774 | // llwarns << "receiveImagePacket " << packet_num << "/" << worker->mLastPacket << " for worker: " << id | ||
1775 | // << " in state: " << LLTextureFetchWorker::sStateDescs[worker->mState] << llendl; | ||
1776 | removeFromNetworkQueue(worker); // failsafe | ||
1777 | mCancelQueue[host].insert(id); | ||
1778 | } | ||
1779 | |||
1780 | worker->unlockWorkData(); | ||
741 | 1781 | ||
742 | U16 packet_num; | 1782 | return res; |
743 | msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num); | 1783 | } |
744 | 1784 | ||
745 | LLTextureFetchWorker* worker = getActiveWorker(id); | 1785 | ////////////////////////////////////////////////////////////////////////////// |
746 | if (!worker) | 1786 | |
1787 | S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, | ||
1788 | U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p) | ||
1789 | { | ||
1790 | S32 state = LLTextureFetchWorker::INVALID; | ||
1791 | F32 data_progress = 0.0f; | ||
1792 | F32 requested_priority = 0.0f; | ||
1793 | F32 fetch_dtime = 999999.f; | ||
1794 | F32 request_dtime = 999999.f; | ||
1795 | U32 fetch_priority = 0; | ||
1796 | |||
1797 | LLMutexLock lock(&mQueueMutex); | ||
1798 | LLTextureFetchWorker* worker = getWorker(id); | ||
1799 | if (worker && worker->haveWork()) | ||
747 | { | 1800 | { |
748 | llwarns << "receiveImageHeader for non active worker: " << id << llendl; | 1801 | worker->lockWorkData(); |
749 | return; | 1802 | state = worker->mState; |
1803 | fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); | ||
1804 | request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); | ||
1805 | if (worker->mFileSize > 0) | ||
1806 | { | ||
1807 | if (state == LLTextureFetchWorker::LOAD_FROM_SIMULATOR) | ||
1808 | { | ||
1809 | S32 data_size = FIRST_PACKET_SIZE + (worker->mLastPacket-1) * MAX_IMG_PACKET_SIZE; | ||
1810 | data_size = llmax(data_size, 0); | ||
1811 | data_progress = (F32)data_size / (F32)worker->mFileSize; | ||
1812 | } | ||
1813 | else if (worker->mFormattedImage.notNull()) | ||
1814 | { | ||
1815 | data_progress = (F32)worker->mFormattedImage->getDataSize() / (F32)worker->mFileSize; | ||
1816 | } | ||
1817 | } | ||
1818 | if (state >= LLTextureFetchWorker::LOAD_FROM_NETWORK && state <= LLTextureFetchWorker::LOAD_FROM_HTTP_GET_DATA) | ||
1819 | { | ||
1820 | requested_priority = worker->mRequestedPriority; | ||
1821 | } | ||
1822 | else | ||
1823 | { | ||
1824 | requested_priority = worker->mImagePriority; | ||
1825 | } | ||
1826 | fetch_priority = worker->getPriority(); | ||
1827 | worker->unlockWorkData(); | ||
750 | } | 1828 | } |
751 | worker->mRequestedTimer.reset(); | 1829 | data_progress_p = data_progress; |
1830 | requested_priority_p = requested_priority; | ||
1831 | fetch_priority_p = fetch_priority; | ||
1832 | fetch_dtime_p = fetch_dtime; | ||
1833 | request_dtime_p = request_dtime; | ||
1834 | return state; | ||
1835 | } | ||
752 | 1836 | ||
753 | U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); | 1837 | void LLTextureFetch::dump() |
754 | if (data_size) | 1838 | { |
1839 | llinfos << "LLTextureFetch REQUESTS:" << llendl; | ||
1840 | for (request_queue_t::iterator iter = mRequestQueue.begin(); | ||
1841 | iter != mRequestQueue.end(); ++iter) | ||
755 | { | 1842 | { |
756 | U8 *data = new U8[data_size]; | 1843 | LLQueuedThread::QueuedRequest* qreq = *iter; |
757 | msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); | 1844 | LLWorkerThread::WorkRequest* wreq = (LLWorkerThread::WorkRequest*)qreq; |
758 | worker->insertPacket(0, data, data_size) | 1845 | LLTextureFetchWorker* worker = (LLTextureFetchWorker*)wreq->getWorkerClass(); |
1846 | llinfos << " ID: " << worker->mID | ||
1847 | << " PRI: " << llformat("0x%08x",wreq->getPriority()) | ||
1848 | << " STATE: " << worker->sStateDescs[worker->mState] | ||
1849 | << llendl; | ||
759 | } | 1850 | } |
760 | } | 1851 | } |
761 | #endif | ||
762 | 1852 | ||
763 | ////////////////////////////////////////////////////////////////////////////// | 1853 | |
1854 | ////////////////////////////////////////////////////////////////////////////// | ||