aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/lltexturefetch.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/lltexturefetch.cpp2049
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
43class LLTextureFetchWorker : public LLWorkerClass 50class LLTextureFetchWorker : public LLWorkerClass
44{ 51{
45 friend class LLTextureFetchImpl; 52 friend class LLTextureFetch;
46
47public:
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
59private: 54private:
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
62public: 217public:
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
69protected: 225protected:
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
72private: 229private:
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
93private: 255private:
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; 338const 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; 356LLTextureFetchWorker::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 410LLTextureFetchWorker::~LLTextureFetchWorker()
158LLTextureFetchWorker::map_t LLTextureFetchWorker::sRequests; 411{
159LLTextureFetchWorker::queue_t LLTextureFetchWorker::sNetworkQueue; 412// llinfos << "Destroy: " << mID
160LLFrameTimer LLTextureFetchWorker::sNetworkTimer; 413// << " Decoded=" << mDecodedDiscard
161LLWorkerThread* LLTextureFetchWorker::sWorkerThread = NULL; 414// << " Requested=" << mRequestedDiscard
162LLMutex* 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 435void LLTextureFetchWorker::clearPackets()
165//static
166void 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 444U32 LLTextureFetchWorker::calcWorkPriority()
173//static
174void 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 453void 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 487void LLTextureFetchWorker::setImagePriority(F32 priority)
218//static
219void 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
500void 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
513void 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()
522bool 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)
281LLTextureFetchWorker* 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
311LLTextureFetchWorker* 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
324LLTextureFetchWorker::LLTextureFetchWorker(const LLUUID& id, // Image UUID 929// Called from MAIN thread
325 const LLHost& host, // Simulator host 930void 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
355LLTextureFetchWorker::~LLTextureFetchWorker() 940//////////////////////////////////////////////////////////////////////////////
941
942// virtual
943void 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
369void LLTextureFetchWorker::clearPackets() 958// virtual
959bool 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
377U32 LLTextureFetchWorker::calcWorkPriority() 997
998void 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
384void LLTextureFetchWorker::setDesiredDiscard(S32 discard) 1006
1007//////////////////////////////////////////////////////////////////////////////
1008
1009bool 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
396void LLTextureFetchWorker::setImagePriority(F32 priority) 1053//////////////////////////////////////////////////////////////////////////////
1054
1055void 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
405void LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) 1074//////////////////////////////////////////////////////////////////////////////
1075
1076void 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//////////////////////////////////////////////////////////////////////////////
427bool LLTextureFetchWorker::doWork(S32 param) 1131
1132void 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 1157void LLTextureFetchWorker::callbackCacheWrite(bool success)
535void 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
539void LLTextureFetchWorker::endWork(S32 param, bool aborted) 1169//////////////////////////////////////////////////////////////////////////////
1170
1171void 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
545bool LLTextureFetchWorker::startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type) 1184bool 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
577bool LLTextureFetchWorker::loadFromVFS() 1227//////////////////////////////////////////////////////////////////////////////
1228
1229bool 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
1255LLTextureFetch::LLTextureFetch(LLTextureCache* cache, bool threaded)
1256 : LLWorkerThread("TextureFetch", threaded),
1257 mDebugCount(0),
1258 mDebugPause(FALSE),
1259 mQueueMutex(getAPRPool()),
1260 mTextureCache(cache)
1261{
1262}
1263
1264LLTextureFetch::~LLTextureFetch()
1265{
1266 // ~LLQueuedThread() called here
1267}
1268
1269bool 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
1331void 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
611bool LLTextureFetchWorker::processSimulatorPackets() 1343// call lockQueue() first!
1344void 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!
1360void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker)
1361{
1362 mNetworkQueue.erase(worker->mID);
1363}
1364
1365// call lockQueue() first!
1366void 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!
1378LLTextureFetchWorker* 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
1390bool 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
1440bool 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
646void LLTextureFetchWorker::startDecode() 1457//virtual
1458S32 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
652bool LLTextureFetchWorker::decodeImage() 1476//////////////////////////////////////////////////////////////////////////////
1477
1478void 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 1645bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
678// static
679void 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(); 1677bool 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/////////////////////////////////////////////////////////////////////////////// 1732bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data)
729// static
730void 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
1787S32 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); 1837void 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//////////////////////////////////////////////////////////////////////////////