diff options
author | Jacek Antonelli | 2008-08-15 23:44:50 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:50 -0500 |
commit | 89fe5dab825a62a0e3fd8d248cbc91c65eb2a426 (patch) | |
tree | bcff14b7888d04a2fec799c59369f6095224bd08 /linden/indra/newview/llviewerimage.cpp | |
parent | Second Life viewer sources 1.13.3.2 (diff) | |
download | meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.zip meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.gz meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.bz2 meta-impy-89fe5dab825a62a0e3fd8d248cbc91c65eb2a426.tar.xz |
Second Life viewer sources 1.14.0.0
Diffstat (limited to 'linden/indra/newview/llviewerimage.cpp')
-rw-r--r-- | linden/indra/newview/llviewerimage.cpp | 1581 |
1 files changed, 374 insertions, 1207 deletions
diff --git a/linden/indra/newview/llviewerimage.cpp b/linden/indra/newview/llviewerimage.cpp index 7500ada..5a745ae 100644 --- a/linden/indra/newview/llviewerimage.cpp +++ b/linden/indra/newview/llviewerimage.cpp | |||
@@ -34,11 +34,13 @@ | |||
34 | #include "llmath.h" | 34 | #include "llmath.h" |
35 | #include "llerror.h" | 35 | #include "llerror.h" |
36 | #include "llgl.h" | 36 | #include "llgl.h" |
37 | #include "llglheaders.h" | ||
37 | #include "llhost.h" | 38 | #include "llhost.h" |
38 | #include "llimage.h" | 39 | #include "llimage.h" |
39 | #include "llimagebmp.h" | 40 | #include "llimagebmp.h" |
40 | #include "llimagej2c.h" | 41 | #include "llimagej2c.h" |
41 | #include "llimagetga.h" | 42 | #include "llimagetga.h" |
43 | #include "llmemtype.h" | ||
42 | #include "llstl.h" | 44 | #include "llstl.h" |
43 | #include "lltexturetable.h" | 45 | #include "lltexturetable.h" |
44 | #include "llvfile.h" | 46 | #include "llvfile.h" |
@@ -47,41 +49,12 @@ | |||
47 | #include "lltimer.h" | 49 | #include "lltimer.h" |
48 | 50 | ||
49 | // viewer includes | 51 | // viewer includes |
52 | #include "lldrawpool.h" | ||
53 | #include "lltexturefetch.h" | ||
50 | #include "llviewerimagelist.h" | 54 | #include "llviewerimagelist.h" |
51 | #include "llviewercontrol.h" | 55 | #include "llviewercontrol.h" |
52 | #include "viewer.h" | ||
53 | #include "llglheaders.h" | ||
54 | #include "pipeline.h" | 56 | #include "pipeline.h" |
55 | #include "lldrawpool.h" | 57 | #include "viewer.h" |
56 | |||
57 | const S32 IMAGE_HEADER_SIZE = 27; | ||
58 | const S32 PACKET_HEADER_SIZE = 4; | ||
59 | |||
60 | /////////////////////////////////////////////////////////////////////////////// | ||
61 | |||
62 | class LLViewerImagePacket | ||
63 | { | ||
64 | public: | ||
65 | LLViewerImagePacket(U8 *data, U16 data_size, U16 packet_num, BOOL wrote_to_disk) | ||
66 | { | ||
67 | mData = data; | ||
68 | mDataSize = data_size; | ||
69 | mPacketNum = packet_num; | ||
70 | mWroteToDisk = wrote_to_disk; | ||
71 | } | ||
72 | |||
73 | ~LLViewerImagePacket() | ||
74 | { | ||
75 | delete[] mData; | ||
76 | } | ||
77 | |||
78 | public: | ||
79 | U8 *mData; | ||
80 | U16 mDataSize; | ||
81 | U16 mPacketNum; | ||
82 | BOOL mWroteToDisk; | ||
83 | }; | ||
84 | |||
85 | 58 | ||
86 | /////////////////////////////////////////////////////////////////////////////// | 59 | /////////////////////////////////////////////////////////////////////////////// |
87 | 60 | ||
@@ -95,6 +68,8 @@ LLPointer<LLImageGL> LLViewerImage::sNullImagep = NULL; | |||
95 | S32 LLViewerImage::sImageCount = 0; | 68 | S32 LLViewerImage::sImageCount = 0; |
96 | LLTimer LLViewerImage::sEvaluationTimer; | 69 | LLTimer LLViewerImage::sEvaluationTimer; |
97 | F32 LLViewerImage::sDesiredDiscardBias = 0.f; | 70 | F32 LLViewerImage::sDesiredDiscardBias = 0.f; |
71 | static F32 sDesiredDiscardBiasMin = -2.0f; // -max number of levels to improve image quality by | ||
72 | static F32 sDesiredDiscardBiasMax = 1.5f; // max number of levels to reduce image quality by | ||
98 | F32 LLViewerImage::sDesiredDiscardScale = 1.1f; | 73 | F32 LLViewerImage::sDesiredDiscardScale = 1.1f; |
99 | S32 LLViewerImage::sBoundTextureMemory = 0; | 74 | S32 LLViewerImage::sBoundTextureMemory = 0; |
100 | S32 LLViewerImage::sTotalTextureMemory = 0; | 75 | S32 LLViewerImage::sTotalTextureMemory = 0; |
@@ -109,6 +84,43 @@ void LLViewerImage::initClass() | |||
109 | LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,3); | 84 | LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,3); |
110 | raw->clear(0x77, 0x77, 0x77, 0xFF); | 85 | raw->clear(0x77, 0x77, 0x77, 0xFF); |
111 | sNullImagep->createGLTexture(0, raw); | 86 | sNullImagep->createGLTexture(0, raw); |
87 | |||
88 | #if 1 | ||
89 | LLViewerImage* imagep = new LLViewerImage(IMG_DEFAULT, TRUE); | ||
90 | sDefaultImagep = imagep; | ||
91 | const S32 dim = 128; | ||
92 | LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3); | ||
93 | U8* data = image_raw->getData(); | ||
94 | for (S32 i = 0; i<dim; i++) | ||
95 | { | ||
96 | for (S32 j = 0; j<dim; j++) | ||
97 | { | ||
98 | #if 0 | ||
99 | const S32 border = 2; | ||
100 | if (i<border || j<border || i>=(dim-border) || j>=(dim-border)) | ||
101 | { | ||
102 | *data++ = 0xff; | ||
103 | *data++ = 0xff; | ||
104 | *data++ = 0xff; | ||
105 | } | ||
106 | else | ||
107 | #endif | ||
108 | { | ||
109 | *data++ = 0x7f; | ||
110 | *data++ = 0x7f; | ||
111 | *data++ = 0x7f; | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | imagep->createGLTexture(0, image_raw); | ||
116 | image_raw = NULL; | ||
117 | gImageList.addImage(imagep); | ||
118 | imagep->dontDiscard(); | ||
119 | #else | ||
120 | sDefaultImagep = gImageList.getImage(IMG_DEFAULT, TRUE, TRUE); | ||
121 | #endif | ||
122 | sSmokeImagep = gImageList.getImage(IMG_SMOKE, TRUE, TRUE); | ||
123 | |||
112 | } | 124 | } |
113 | 125 | ||
114 | // static | 126 | // static |
@@ -171,6 +183,7 @@ void LLViewerImage::updateClass(const F32 velocity, const F32 angular_velocity) | |||
171 | sEvaluationTimer.reset(); | 183 | sEvaluationTimer.reset(); |
172 | } | 184 | } |
173 | } | 185 | } |
186 | sDesiredDiscardBias = llclamp(sDesiredDiscardBias, sDesiredDiscardBiasMin, sDesiredDiscardBiasMax); | ||
174 | } | 187 | } |
175 | 188 | ||
176 | //---------------------------------------------------------------------------- | 189 | //---------------------------------------------------------------------------- |
@@ -209,35 +222,20 @@ LLViewerImage::LLViewerImage(const LLImageRaw* raw, BOOL usemipmaps) | |||
209 | 222 | ||
210 | void LLViewerImage::init(bool firstinit) | 223 | void LLViewerImage::init(bool firstinit) |
211 | { | 224 | { |
212 | mDataCodec = 0; | ||
213 | mFullWidth = 0; | 225 | mFullWidth = 0; |
214 | mFullHeight = 0; | 226 | mFullHeight = 0; |
215 | mFormattedImagep = NULL; | ||
216 | mNeedsAux = FALSE; | 227 | mNeedsAux = FALSE; |
217 | mRequested = FALSE; | ||
218 | mNeedsDecode = FALSE; | ||
219 | mTexelsPerImage = 64.f*64.f; | 228 | mTexelsPerImage = 64.f*64.f; |
220 | mMaxVirtualSize = 0.f; | 229 | mMaxVirtualSize = 0.f; |
230 | mDiscardVirtualSize = 0.f; | ||
221 | mMaxCosAngle = -1.f; | 231 | mMaxCosAngle = -1.f; |
222 | mRequestedDiscardLevel = -1; | 232 | mRequestedDiscardLevel = -1; |
223 | mRequestedDownloadPriority = 0.f; | 233 | mRequestedDownloadPriority = 0.f; |
224 | mPackets = 0; | ||
225 | mGotFirstPacket = FALSE; | ||
226 | mPacketsReceived = 0; | ||
227 | mFullyLoaded = FALSE; | 234 | mFullyLoaded = FALSE; |
228 | mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; | 235 | mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; |
229 | mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; | 236 | mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; |
230 | mStreamFile = NULL; | 237 | mCalculatedDiscardLevel = -1.f; |
231 | mCachedData = NULL; | ||
232 | mCachedSize = 0; | ||
233 | mFormattedFlushed = FALSE; | ||
234 | mTotalBytes = 0; | ||
235 | 238 | ||
236 | resetPacketData(); | ||
237 | |||
238 | mLastPacketProcessed = -1; | ||
239 | mLastBytesProcessed = 0; | ||
240 | mLastPacket = -1; | ||
241 | mDecodingAux = FALSE; | 239 | mDecodingAux = FALSE; |
242 | 240 | ||
243 | mKnownDrawWidth = 0; | 241 | mKnownDrawWidth = 0; |
@@ -247,7 +245,6 @@ void LLViewerImage::init(bool firstinit) | |||
247 | { | 245 | { |
248 | mDecodePriority = 0.f; | 246 | mDecodePriority = 0.f; |
249 | mInImageList = 0; | 247 | mInImageList = 0; |
250 | mInStaticVFS = FALSE; | ||
251 | } | 248 | } |
252 | mIsMediaTexture = FALSE; | 249 | mIsMediaTexture = FALSE; |
253 | 250 | ||
@@ -261,9 +258,18 @@ void LLViewerImage::init(bool firstinit) | |||
261 | 258 | ||
262 | mIsRawImageValid = FALSE; | 259 | mIsRawImageValid = FALSE; |
263 | mRawDiscardLevel = INVALID_DISCARD_LEVEL; | 260 | mRawDiscardLevel = INVALID_DISCARD_LEVEL; |
264 | mRawImage = NULL; | 261 | mMinDiscardLevel = 0; |
265 | 262 | ||
266 | mTargetHost = LLHost::invalid; | 263 | mTargetHost = LLHost::invalid; |
264 | |||
265 | mHasFetcher = FALSE; | ||
266 | mIsFetching = FALSE; | ||
267 | mFetchState = 0; | ||
268 | mFetchPriority = 0; | ||
269 | mDownloadProgress = 0.f; | ||
270 | mFetchDeltaTime = 999999.f; | ||
271 | mDecodeFrame = 0; | ||
272 | mVisibleFrame = 0; | ||
267 | } | 273 | } |
268 | 274 | ||
269 | // virtual | 275 | // virtual |
@@ -283,6 +289,10 @@ void LLViewerImage::dump() | |||
283 | 289 | ||
284 | LLViewerImage::~LLViewerImage() | 290 | LLViewerImage::~LLViewerImage() |
285 | { | 291 | { |
292 | if (mHasFetcher) | ||
293 | { | ||
294 | gTextureFetch->deleteRequest(getID(), true); | ||
295 | } | ||
286 | // Explicitly call LLViewerImage::cleanup since we're in a destructor and cleanup is virtual | 296 | // Explicitly call LLViewerImage::cleanup since we're in a destructor and cleanup is virtual |
287 | LLViewerImage::cleanup(); | 297 | LLViewerImage::cleanup(); |
288 | sImageCount--; | 298 | sImageCount--; |
@@ -304,27 +314,9 @@ void LLViewerImage::cleanup() | |||
304 | } | 314 | } |
305 | mLoadedCallbackList.clear(); | 315 | mLoadedCallbackList.clear(); |
306 | 316 | ||
307 | // Clean up any remaining packet data. | ||
308 | std::for_each(mReceivedPacketMap.begin(), mReceivedPacketMap.end(), DeletePairedPointer()); | ||
309 | mReceivedPacketMap.clear(); | ||
310 | |||
311 | // Clean up the streaming file | ||
312 | if (mStreamFile && !mStreamFile->isReadComplete()) | ||
313 | { | ||
314 | // llwarns << "Destroying LLViewerImage stream file while still reading data!" << llendl; | ||
315 | } | ||
316 | delete mStreamFile; | ||
317 | mStreamFile = NULL; | ||
318 | |||
319 | // Clean up image data | 317 | // Clean up image data |
320 | setFormattedImage(NULL); | 318 | destroyRawImage(); |
321 | mRawImage = NULL; | ||
322 | mIsRawImageValid = FALSE; | ||
323 | mAuxRawImage = NULL; | ||
324 | 319 | ||
325 | delete[] mCachedData; | ||
326 | mCachedData = NULL; | ||
327 | |||
328 | // LLImageGL::cleanup will get called more than once when this is used in the destructor. | 320 | // LLImageGL::cleanup will get called more than once when this is used in the destructor. |
329 | LLImageGL::cleanup(); | 321 | LLImageGL::cleanup(); |
330 | } | 322 | } |
@@ -335,128 +327,25 @@ void LLViewerImage::reinit(BOOL usemipmaps /* = TRUE */) | |||
335 | LLImageGL::init(usemipmaps); | 327 | LLImageGL::init(usemipmaps); |
336 | init(false); | 328 | init(false); |
337 | setSize(0,0,0); | 329 | setSize(0,0,0); |
338 | if (mInStaticVFS) | ||
339 | { | ||
340 | mFormattedFlushed = TRUE; | ||
341 | } | ||
342 | } | 330 | } |
343 | 331 | ||
344 | /////////////////////////////////////////////////////////////////////////////// | 332 | /////////////////////////////////////////////////////////////////////////////// |
345 | 333 | ||
346 | void LLViewerImage::setFormattedImage(LLImageFormatted* imagep) | 334 | // ONLY called from LLViewerImageList |
347 | { | ||
348 | mFormattedImagep = NULL; // deletes image | ||
349 | mFormattedImagep = imagep; | ||
350 | if (mFormattedImagep.notNull()) | ||
351 | { | ||
352 | mFormattedImagep->mMemType = LLMemType::MTYPE_APPFMTIMAGE; | ||
353 | mFormattedFlushed = FALSE; | ||
354 | } | ||
355 | else | ||
356 | { | ||
357 | setNeedsDecode(FALSE); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | BOOL LLViewerImage::loadLocalImage(const LLUUID &image_id) | ||
362 | { | ||
363 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | ||
364 | |||
365 | // first look for this image in the static VFS | ||
366 | LLAssetType::EType asset_type = LLAssetType::AT_NONE; | ||
367 | // Try TGA first | ||
368 | if (gStaticVFS->getExists(image_id, LLAssetType::AT_TEXTURE_TGA)) | ||
369 | { | ||
370 | asset_type = LLAssetType::AT_TEXTURE_TGA; | ||
371 | //RN: force disable discards for TGA files because they can't decode at different quality levels | ||
372 | dontDiscard(); | ||
373 | mDataCodec = IMG_CODEC_TGA; | ||
374 | } | ||
375 | else if (gStaticVFS->getExists(image_id, LLAssetType::AT_TEXTURE)) | ||
376 | { | ||
377 | // then try for a J2C version | ||
378 | asset_type = LLAssetType::AT_TEXTURE; | ||
379 | mDataCodec = IMG_CODEC_J2C; | ||
380 | LLImageJ2C* imagej2c = new LLImageJ2C(); | ||
381 | setFormattedImage(imagej2c); | ||
382 | } | ||
383 | |||
384 | if (asset_type != LLAssetType::AT_NONE) | ||
385 | { | ||
386 | S32 size = gStaticVFS->getSize(image_id, asset_type); | ||
387 | U8* buffer = new U8[size]; | ||
388 | BOOL success = LLVFSThread::sLocal->readImmediate(gStaticVFS, image_id, asset_type, buffer, 0, size); | ||
389 | |||
390 | if (!success) | ||
391 | { | ||
392 | llwarns << "loadLocalImage() - vfs read failed" << llendl; | ||
393 | return FALSE; | ||
394 | } | ||
395 | |||
396 | mInStaticVFS = TRUE; | ||
397 | mFullyLoaded = TRUE; | ||
398 | setNeedsDecode(TRUE); // Loading a local image | ||
399 | mID = image_id; | ||
400 | setDecodeData(buffer, size); | ||
401 | mTotalBytes = size; | ||
402 | mLastBytesProcessed = size; | ||
403 | return TRUE; | ||
404 | } | ||
405 | |||
406 | return FALSE; | ||
407 | } | ||
408 | |||
409 | BOOL LLViewerImage::startVFSLoad() | ||
410 | { | ||
411 | // We're no longer considered "flushed" no matter what happens after here. | ||
412 | mFormattedFlushed = FALSE; | ||
413 | if (!mStreamFile && mFormattedImagep.isNull()) | ||
414 | { | ||
415 | // Start load from VFS if it's there | ||
416 | if (gVFS->getExists(mID, LLAssetType::AT_TEXTURE)) | ||
417 | { | ||
418 | // llinfos << "Reading image from disk " << getID() << llendl; | ||
419 | |||
420 | //llinfos << mID << ": starting VFS load" << llendl; | ||
421 | mStreamFile = new LLVFile(gVFS, mID, LLAssetType::AT_TEXTURE, LLVFile::READ_WRITE); | ||
422 | mCachedSize = 0; | ||
423 | gImageList.mLoadingStreamList.push_back(this); | ||
424 | } | ||
425 | else | ||
426 | { | ||
427 | return loadLocalImage(mID); | ||
428 | } | ||
429 | } | ||
430 | return TRUE; | ||
431 | } | ||
432 | |||
433 | void LLViewerImage::startImageDecode() | ||
434 | { | ||
435 | // We need to load and/or decode the image | ||
436 | if (mFormattedImagep.isNull()) | ||
437 | { | ||
438 | startVFSLoad(); // Start the VFS loading | ||
439 | } | ||
440 | else | ||
441 | { | ||
442 | setNeedsDecode(TRUE); // Force a new decode of this texture | ||
443 | } | ||
444 | } | ||
445 | |||
446 | |||
447 | BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) | 335 | BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) |
448 | { | 336 | { |
449 | if (mFormattedImagep.notNull() && mFormattedImagep->isDecoding()) | 337 | if (!mNeedsCreateTexture) |
450 | { | 338 | { |
451 | llerrs << "Trying to create texture on an image that is currently being decoded: " << mID << llendl; | 339 | destroyRawImage(); |
340 | return FALSE; | ||
452 | } | 341 | } |
453 | mNeedsCreateTexture = FALSE; | 342 | mNeedsCreateTexture = FALSE; |
454 | if (mRawImage.isNull()) | 343 | if (mRawImage.isNull()) |
455 | { | 344 | { |
456 | llerrs << "LLViewerImage trying to create texture with no Raw Image" << llendl; | 345 | llerrs << "LLViewerImage trying to create texture with no Raw Image" << llendl; |
457 | } | 346 | } |
458 | // llinfos << llformat("IMAGE Creating (%d,%d) [%d x %d] Bytes: %d ", | 347 | // llinfos << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ", |
459 | // mRawDiscardLevel, mFormattedImagep ? mFormattedImagep->getDiscardLevel() : -1, | 348 | // mRawDiscardLevel, |
460 | // mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize()) | 349 | // mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize()) |
461 | // << mID.getString() << llendl; | 350 | // << mID.getString() << llendl; |
462 | BOOL res = TRUE; | 351 | BOOL res = TRUE; |
@@ -471,7 +360,7 @@ BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) | |||
471 | // A non power-of-two image was uploaded (through a non standard client) | 360 | // A non power-of-two image was uploaded (through a non standard client) |
472 | // We treat these images as missing assets which causes them to | 361 | // We treat these images as missing assets which causes them to |
473 | // be renderd as 'missing image' and to stop requesting data | 362 | // be renderd as 'missing image' and to stop requesting data |
474 | setIsMissingAsset(TRUE); | 363 | setIsMissingAsset(); |
475 | destroyRawImage(); | 364 | destroyRawImage(); |
476 | return FALSE; | 365 | return FALSE; |
477 | } | 366 | } |
@@ -481,6 +370,7 @@ BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) | |||
481 | // Iterate through the list of image loading callbacks to see | 370 | // Iterate through the list of image loading callbacks to see |
482 | // what sort of data they need. | 371 | // what sort of data they need. |
483 | // | 372 | // |
373 | // *TODO: Fix image callback code | ||
484 | BOOL imageraw_callbacks = FALSE; | 374 | BOOL imageraw_callbacks = FALSE; |
485 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); | 375 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); |
486 | iter != mLoadedCallbackList.end(); ) | 376 | iter != mLoadedCallbackList.end(); ) |
@@ -500,292 +390,6 @@ BOOL LLViewerImage::createTexture(S32 usename/*= 0*/) | |||
500 | return res; | 390 | return res; |
501 | } | 391 | } |
502 | 392 | ||
503 | BOOL LLViewerImage::destroyTexture() | ||
504 | { | ||
505 | LLImageGL::destroyGLTexture(); | ||
506 | return TRUE; | ||
507 | } | ||
508 | |||
509 | void LLViewerImage::resetPacketData() | ||
510 | { | ||
511 | //llinfos << "resetting packet data for " << getID() << llendl; | ||
512 | mPackets = 0; | ||
513 | mLastPacket = -1; | ||
514 | mPacketsReceived = 0; | ||
515 | mGotFirstPacket = FALSE; | ||
516 | mRequested = FALSE; | ||
517 | |||
518 | std::for_each(mReceivedPacketMap.begin(), mReceivedPacketMap.end(), DeletePairedPointer()); | ||
519 | mReceivedPacketMap.clear(); | ||
520 | mLastPacketProcessed = -1; | ||
521 | } | ||
522 | |||
523 | //////////////////////////////////////////////////////////////////////////////// | ||
524 | // This sets up a new formatted image at the requested size, and sets the decode flag | ||
525 | void LLViewerImage::setDecodeData(U8 * data, U32 size) | ||
526 | { | ||
527 | if (size == mTotalBytes) | ||
528 | { | ||
529 | mFullyLoaded = TRUE; | ||
530 | } | ||
531 | if (mDataCodec == IMG_CODEC_J2C) | ||
532 | { | ||
533 | //Codec 2 = compressed with JPEG2000 (Wavelet) | ||
534 | // Create formatted image first, then use it to generate the | ||
535 | // raw image. | ||
536 | if (mFormattedImagep.isNull()) | ||
537 | { | ||
538 | LLPointer<LLImageJ2C> j2cp = new LLImageJ2C(); | ||
539 | setFormattedImage(j2cp); | ||
540 | } | ||
541 | BOOL res = mFormattedImagep->setData(data, size); | ||
542 | if (mFullyLoaded) | ||
543 | { | ||
544 | mFormattedImagep->setDiscardLevel(0); // Force full res if all data is loaded | ||
545 | } | ||
546 | if ((mFormattedImagep->getWidth() > MAX_IMAGE_SIZE_DEFAULT || | ||
547 | mFormattedImagep->getHeight() > MAX_IMAGE_SIZE_DEFAULT) && | ||
548 | (mFormattedImagep->getDiscardLevel() == 0)) | ||
549 | { | ||
550 | mFormattedImagep->setDiscardLevel(1); // Force x2048 images to x1024 | ||
551 | } | ||
552 | |||
553 | if( res ) | ||
554 | { | ||
555 | if (mFormattedImagep->getComponents() > 4) | ||
556 | { | ||
557 | mNeedsAux = TRUE; | ||
558 | } | ||
559 | else | ||
560 | { | ||
561 | mNeedsAux = FALSE; | ||
562 | } | ||
563 | mFullWidth = mFormattedImagep->getWidth(); | ||
564 | mFullHeight = mFormattedImagep->getHeight(); | ||
565 | if ((mFullWidth == 0) || (mFullHeight == 0)) | ||
566 | { | ||
567 | llwarns << "Zero size width/height!" << llendl; | ||
568 | } | ||
569 | setNeedsDecode(TRUE); // Setting new formatted data | ||
570 | } | ||
571 | else | ||
572 | { | ||
573 | llwarns << "Unable to setData() for image " << mID << " Aborting." << llendl; | ||
574 | abortDecode(); | ||
575 | } | ||
576 | } | ||
577 | else if (mDataCodec == IMG_CODEC_TGA) | ||
578 | { | ||
579 | //Codec 4 = compressed with TGA | ||
580 | // Create formatted image first, then use it to generate the | ||
581 | // raw image. | ||
582 | if (mFormattedImagep.isNull()) | ||
583 | { | ||
584 | LLPointer<LLImageTGA> tgap = new LLImageTGA(); | ||
585 | setFormattedImage(tgap); | ||
586 | } | ||
587 | BOOL res = mFormattedImagep->setData(data, size); | ||
588 | |||
589 | if( res ) | ||
590 | { | ||
591 | mFullWidth = mFormattedImagep->getWidth(); | ||
592 | mFullHeight = mFormattedImagep->getHeight(); | ||
593 | if ((mFullWidth == 0) || (mFullHeight == 0)) | ||
594 | { | ||
595 | llwarns << "Zero size width/height!" << llendl; | ||
596 | } | ||
597 | setNeedsDecode(TRUE); // Setting new formatted data | ||
598 | } | ||
599 | else | ||
600 | { | ||
601 | llwarns << "Unable to setData() for image " << mID << " Aborting." << llendl; | ||
602 | abortDecode(); | ||
603 | } | ||
604 | } | ||
605 | else | ||
606 | { | ||
607 | llerrs << "Image " << mID << ": Unknown codec " << (int)mDataCodec << llendl; | ||
608 | setNeedsDecode(FALSE); // Unknown codec | ||
609 | } | ||
610 | } | ||
611 | |||
612 | void LLViewerImage::decodeImage(const F32 decode_time) | ||
613 | { | ||
614 | if (!needsDecode()) | ||
615 | { | ||
616 | return; | ||
617 | } | ||
618 | |||
619 | if (mFormattedImagep.isNull()) | ||
620 | { | ||
621 | llerrs << "Decoding image without formatted data!" << llendl; | ||
622 | return; | ||
623 | } | ||
624 | |||
625 | // | ||
626 | // Only do a decode if we don't already have an image for this resolution. | ||
627 | // | ||
628 | if (getTexName() != 0 && getDiscardLevel() <= mFormattedImagep->getDiscardLevel() | ||
629 | && !mNeedsAux) | ||
630 | { | ||
631 | // We already have an image this size or larger | ||
632 | setNeedsDecode(FALSE); | ||
633 | return; | ||
634 | } | ||
635 | |||
636 | // Partial Decode of J2C images: | ||
637 | // If this is the first time we are decoding an image, | ||
638 | // make sure we limit the amount of data we decode in order not to stall other decodes | ||
639 | if (!mFormattedImagep->isDecoding() && mFormattedImagep->getCodec() == IMG_CODEC_J2C) | ||
640 | { | ||
641 | LLImageJ2C* j2cp = (LLImageJ2C*)((LLImageFormatted*)mFormattedImagep); | ||
642 | const S32 INITIAL_DECODE_SIZE = 2048; | ||
643 | if (!mDontDiscard && | ||
644 | getUseMipMaps() && | ||
645 | !mNeedsAux && | ||
646 | getDiscardLevel() < 0 && | ||
647 | mFormattedImagep->getDataSize() > INITIAL_DECODE_SIZE * 2) | ||
648 | { | ||
649 | j2cp->setMaxBytes(INITIAL_DECODE_SIZE); | ||
650 | } | ||
651 | else | ||
652 | { | ||
653 | j2cp->setMaxBytes(0); // In case we set it on a previous decode | ||
654 | } | ||
655 | } | ||
656 | |||
657 | // | ||
658 | // Decode Image | ||
659 | // | ||
660 | mLastDecodeTime.reset(); | ||
661 | |||
662 | if (mFormattedImagep->getCodec() == 0) | ||
663 | { | ||
664 | llerrs << "LLViewerImage::decodeImage: mFormattedImagep->getCodec() == 0" << llendl; | ||
665 | } | ||
666 | |||
667 | // | ||
668 | // Decode first 4 channels | ||
669 | // | ||
670 | // Skip over this if we're already in the process of decoding the aux channel, | ||
671 | // that means that we've alredy decoded the base channels of this texture. | ||
672 | if (!mDecodingAux) | ||
673 | { | ||
674 | if (!mFormattedImagep->isDecoding()) | ||
675 | { | ||
676 | mNeedsCreateTexture = FALSE; // Raw is no longer valid | ||
677 | destroyRawImage(); | ||
678 | createRawImage(mFormattedImagep->getDiscardLevel()); | ||
679 | //llinfos << "starting decode at " << (S32)mFormattedImagep->getDiscardLevel() << " for " << getID() << llendl; | ||
680 | } | ||
681 | else | ||
682 | { | ||
683 | llassert(mRawImage.notNull()); | ||
684 | } | ||
685 | if (!mFormattedImagep->decode(mRawImage, decode_time, 0, 4)) | ||
686 | { | ||
687 | if (!mFormattedImagep->isDecoding()) | ||
688 | { | ||
689 | // bogus data, delete and try again | ||
690 | llwarns << "Failed to decode " << mID << ":" << gTextureTable.getName(mID) << llendl; | ||
691 | abortDecode(); | ||
692 | destroyRawImage(); | ||
693 | return; | ||
694 | } | ||
695 | } | ||
696 | // llinfos << llformat("IMAGE Decode (%d) ", mFormattedImagep->getDiscardLevel()) << mID << llendl; | ||
697 | |||
698 | // Get the discard level of the decoded raw image, | ||
699 | // which may not match the formatted image discard level if a partial decode was done | ||
700 | mRawDiscardLevel = mFormattedImagep->getRawDiscardLevel(); | ||
701 | |||
702 | if (mFormattedImagep->isDecoding()) | ||
703 | { | ||
704 | return; // Not done decoding. | ||
705 | } | ||
706 | } | ||
707 | |||
708 | // | ||
709 | // If we've finished with the main channels and need to decode the aux, do it now. | ||
710 | // Aux buffers contain extra data (e.g. cloth maps) | ||
711 | // | ||
712 | if (mNeedsAux) | ||
713 | { | ||
714 | mDecodingAux = TRUE; | ||
715 | |||
716 | // Create the target raw image for the aux channels | ||
717 | if (mAuxRawImage.isNull()) | ||
718 | { | ||
719 | S32 discard = mFormattedImagep->getDiscardLevel(); | ||
720 | mAuxRawImage = new LLImageRaw(getWidth(discard), getHeight(discard), 1); | ||
721 | mAuxRawImage->mMemType = LLMemType::MTYPE_APPAUXRAWIMAGE; | ||
722 | } | ||
723 | |||
724 | if (!mFormattedImagep->decode(mAuxRawImage, decode_time, 4, 4)) | ||
725 | { | ||
726 | if (!mFormattedImagep->isDecoding()) | ||
727 | { | ||
728 | llwarns << "Failed to decode high components " << mID << ":" << gTextureTable.getName(mID) << llendl; | ||
729 | abortDecode(); | ||
730 | destroyRawImage(); | ||
731 | return; // decode failed; re-request | ||
732 | } | ||
733 | } | ||
734 | |||
735 | if (mFormattedImagep->isDecoding()) | ||
736 | { | ||
737 | return; // Not done decoding. | ||
738 | } | ||
739 | |||
740 | mDecodingAux = FALSE; | ||
741 | } | ||
742 | |||
743 | if (mRawImage.notNull() && getComponents() != mRawImage->getComponents()) | ||
744 | { | ||
745 | // | ||
746 | // We've changed the number of components (presumably this is after | ||
747 | // decoding the first packet of an image), so we need to move any | ||
748 | // objects using this pool to a different pool. | ||
749 | // | ||
750 | mComponents = mRawImage->getComponents(); | ||
751 | gPipeline.dirtyPoolObjectTextures(this); | ||
752 | } | ||
753 | |||
754 | // | ||
755 | // We've decoded this image, and no longer need to. | ||
756 | // | ||
757 | setNeedsDecode(FALSE); // Done decoding image | ||
758 | mIsRawImageValid = TRUE; | ||
759 | |||
760 | // | ||
761 | // We have a raw image, and now we need to push the data | ||
762 | // from the raw image into the GL image | ||
763 | // | ||
764 | llassert(mRawImage.notNull()); | ||
765 | mNeedsCreateTexture = TRUE; | ||
766 | |||
767 | // Everything's OK... | ||
768 | #if LL_DEBUG | ||
769 | lldebugst(LLERR_IMAGE) << "Img: "; | ||
770 | std::string tex_name = gTextureTable.getName(mID); | ||
771 | if (!tex_name.empty()) | ||
772 | { | ||
773 | llcont << tex_name; | ||
774 | } | ||
775 | else | ||
776 | { | ||
777 | llcont << mID; | ||
778 | } | ||
779 | llcont << " Discard level " << (S32)getDiscardLevel(); | ||
780 | llcont << llendl; | ||
781 | #endif | ||
782 | } | ||
783 | |||
784 | bool LLViewerImage::isDecoding() | ||
785 | { | ||
786 | return (mFormattedImagep.notNull() && mFormattedImagep->isDecoding()); | ||
787 | } | ||
788 | |||
789 | //============================================================================ | 393 | //============================================================================ |
790 | 394 | ||
791 | void LLViewerImage::addTextureStats(F32 pixel_area, | 395 | void LLViewerImage::addTextureStats(F32 pixel_area, |
@@ -868,15 +472,23 @@ void LLViewerImage::processTextureStats() | |||
868 | } | 472 | } |
869 | else | 473 | else |
870 | { | 474 | { |
871 | // Guess the required scale factor of the image using pixels per texel.. | 475 | if ((mCalculatedDiscardLevel >= 0.f) && |
872 | // Right now, use a safe 1:1 for the tradeoff - we can adjust this later. | 476 | (llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f)) |
873 | // Actually, it might be nice to generate a float, so we can prioritize which | 477 | { |
874 | // ones we can discard quality levels from. | 478 | // < 20% change in virtual size = no change in desired discard |
875 | discard_level = (F32)(log(mTexelsPerImage/mMaxVirtualSize) / log_4); | 479 | discard_level = mCalculatedDiscardLevel; |
480 | } | ||
481 | else | ||
482 | { | ||
483 | // Calculate the required scale factor of the image using pixels per texel | ||
484 | discard_level = (F32)(log(mTexelsPerImage/mMaxVirtualSize) / log_4); | ||
485 | mDiscardVirtualSize = mMaxVirtualSize; | ||
486 | mCalculatedDiscardLevel = discard_level; | ||
487 | } | ||
876 | } | 488 | } |
877 | if (mBoostLevel < LLViewerImage::BOOST_HIGH) | 489 | if (mBoostLevel < LLViewerImage::BOOST_HIGH) |
878 | { | 490 | { |
879 | static const F32 discard_bias = 0.5f; // Must be < 1 or highest discard will never load! | 491 | static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load! |
880 | discard_level += discard_bias; | 492 | discard_level += discard_bias; |
881 | discard_level += sDesiredDiscardBias; | 493 | discard_level += sDesiredDiscardBias; |
882 | discard_level *= sDesiredDiscardScale; // scale | 494 | discard_level *= sDesiredDiscardScale; // scale |
@@ -901,94 +513,68 @@ void LLViewerImage::processTextureStats() | |||
901 | // proper action if we don't. | 513 | // proper action if we don't. |
902 | // | 514 | // |
903 | 515 | ||
904 | // | ||
905 | // Only need to do an actual decode if we don't have the right GL level, | ||
906 | // or we don't have raw data and need raw data. | ||
907 | // | ||
908 | BOOL increase_discard = FALSE; | 516 | BOOL increase_discard = FALSE; |
909 | if (getDiscardLevel() < 0 || mDesiredDiscardLevel < getDiscardLevel()) | 517 | S32 current_discard = getDiscardLevel(); |
518 | if ((sDesiredDiscardBias > 0.0f) && | ||
519 | (current_discard >= 0 && mDesiredDiscardLevel >= current_discard)) | ||
910 | { | 520 | { |
911 | // We need to do a decode of that discard level to get more data. | 521 | if ( sBoundTextureMemory > sMaxBoundTextureMem*texmem_middle_bound_scale) |
912 | if (mFormattedImagep.notNull() && !needsDecode()) | ||
913 | { | 522 | { |
914 | if (mFormattedImagep->getDiscardLevel() <= mDesiredDiscardLevel) | 523 | // Limit the amount of GL memory bound each frame |
524 | if (mDesiredDiscardLevel > current_discard) | ||
915 | { | 525 | { |
916 | setNeedsDecode(TRUE); // processTextureStats - Changing discard level of texture | 526 | increase_discard = TRUE; |
917 | } | 527 | } |
918 | // else let the llviewerimagelist logic do its thing | ||
919 | } | 528 | } |
920 | else if (mFormattedFlushed) | 529 | if ( sTotalTextureMemory > sMaxTotalTextureMem*texmem_middle_bound_scale) |
921 | { | 530 | { |
922 | //llinfos << "Attempting vfs reload (a) of " << mID << llendl; | 531 | // Only allow GL to have 2x the video card memory |
923 | startVFSLoad(); | 532 | if (!getBoundRecently()) |
924 | } | 533 | { |
925 | } | 534 | increase_discard = TRUE; |
926 | else if (sDesiredDiscardBias > 0.0f && sBoundTextureMemory > sMaxBoundTextureMem*texmem_middle_bound_scale) | 535 | } |
927 | { | ||
928 | // Limit the amount of GL memory bound each frame | ||
929 | if (mDesiredDiscardLevel > getDiscardLevel()) | ||
930 | { | ||
931 | increase_discard = TRUE; | ||
932 | } | 536 | } |
933 | } | 537 | if (increase_discard) |
934 | else if (sDesiredDiscardBias > 0.0f && sTotalTextureMemory > sTotalTextureMemory*texmem_middle_bound_scale) | ||
935 | { | ||
936 | // Only allow GL to have 2x the video card memory | ||
937 | if (!getBoundRecently()) | ||
938 | { | 538 | { |
939 | increase_discard = TRUE; | 539 | // llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl; |
540 | sBoundTextureMemory -= mTextureMemory; | ||
541 | sTotalTextureMemory -= mTextureMemory; | ||
542 | // Increase the discard level (reduce the texture res) | ||
543 | S32 new_discard = current_discard+1; | ||
544 | setDiscardLevel(new_discard); | ||
545 | sBoundTextureMemory += mTextureMemory; | ||
546 | sTotalTextureMemory += mTextureMemory; | ||
940 | } | 547 | } |
941 | } | 548 | } |
942 | if (increase_discard) | ||
943 | { | ||
944 | sBoundTextureMemory -= mTextureMemory; | ||
945 | sTotalTextureMemory -= mTextureMemory; | ||
946 | // Increase the discard level (reduce the texture res) | ||
947 | S32 new_discard = getDiscardLevel()+1; | ||
948 | setDiscardLevel(new_discard); | ||
949 | sBoundTextureMemory += mTextureMemory; | ||
950 | sTotalTextureMemory += mTextureMemory; | ||
951 | } | ||
952 | } | 549 | } |
953 | |||
954 | // | ||
955 | // Flush the formatted data for this image if it hasn't been used in a while. | ||
956 | // | ||
957 | #if 1 | ||
958 | const F32 FLUSH_TIME = 30.f; | ||
959 | if (mFormattedImagep.notNull()) | ||
960 | { | ||
961 | if ((mLastDecodeTime.getElapsedTimeF32() > FLUSH_TIME) | ||
962 | && !mStreamFile | ||
963 | && (this->mLastPacketTimer.getElapsedTimeF32() > FLUSH_TIME) | ||
964 | && !mNeedsDecode | ||
965 | && !mFormattedImagep->isDecoding() | ||
966 | && (mDesiredDiscardLevel == getDiscardLevel())) | ||
967 | { | ||
968 | //llinfos << mID << ": flushing formatted image, last processed packet " << mLastPacketProcessed << llendl; | ||
969 | // Treat this very similarly to like we haven't gotten any data | ||
970 | // Flush all received packets that haven't been processed, they will be readded on reload. | ||
971 | resetPacketData(); | ||
972 | |||
973 | setFormattedImage(NULL); | ||
974 | mFullyLoaded = FALSE; | ||
975 | |||
976 | // This flag says that we may have VFS data if we want to reload this texture | ||
977 | // (or get network traffic for this texture) | ||
978 | mFormattedFlushed = TRUE; | ||
979 | } | ||
980 | } | ||
981 | #endif | ||
982 | } | 550 | } |
983 | 551 | ||
984 | //============================================================================ | 552 | //============================================================================ |
985 | 553 | ||
986 | F32 LLViewerImage::calcDecodePriority() | 554 | F32 LLViewerImage::calcDecodePriority() |
987 | { | 555 | { |
988 | F32 priority; | 556 | #ifndef LL_RELEASE_FOR_DOWNLOAD |
989 | S32 gldiscard = getDiscardLevel(); | 557 | if (mID == gTextureFetch->mDebugID) |
990 | S32 ddiscard = gldiscard - mDesiredDiscardLevel; | 558 | { |
559 | gTextureFetch->mDebugCount++; // for setting breakpoints | ||
560 | } | ||
561 | #endif | ||
562 | |||
563 | if (mNeedsCreateTexture) | ||
564 | { | ||
565 | return mDecodePriority; // no change while waiting to create | ||
566 | } | ||
991 | 567 | ||
568 | F32 priority; | ||
569 | S32 cur_discard = getDiscardLevel(); | ||
570 | F32 pixel_priority = fsqrtf(mMaxVirtualSize) * (1.f + mMaxCosAngle); | ||
571 | const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame | ||
572 | mDecodeFrame++; | ||
573 | if (pixel_priority > 0.f) | ||
574 | { | ||
575 | mVisibleFrame = mDecodeFrame; | ||
576 | } | ||
577 | |||
992 | if (mIsMissingAsset) | 578 | if (mIsMissingAsset) |
993 | { | 579 | { |
994 | priority = 0.0f; | 580 | priority = 0.0f; |
@@ -996,50 +582,73 @@ F32 LLViewerImage::calcDecodePriority() | |||
996 | else if (mDesiredDiscardLevel > mMaxDiscardLevel) | 582 | else if (mDesiredDiscardLevel > mMaxDiscardLevel) |
997 | { | 583 | { |
998 | // Don't decode anything we don't need | 584 | // Don't decode anything we don't need |
999 | priority = 0.0f; | 585 | priority = -1.0f; |
586 | } | ||
587 | else if (pixel_priority <= 0.f && (cur_discard < 0 || mDesiredDiscardLevel < cur_discard)) | ||
588 | { | ||
589 | // Not on screen but we might want some data | ||
590 | if (mBoostLevel > BOOST_HIGH) | ||
591 | { | ||
592 | // Always want high boosted images | ||
593 | priority = 1.f; | ||
594 | } | ||
595 | else if (mVisibleFrame == 0 || (mDecodeFrame - mVisibleFrame > MIN_NOT_VISIBLE_FRAMES)) | ||
596 | { | ||
597 | // Don't decode anything that isn't visible unless it's important | ||
598 | priority = -2.0f; | ||
599 | } | ||
600 | else | ||
601 | { | ||
602 | // Leave the priority as-is | ||
603 | return mDecodePriority; | ||
604 | } | ||
1000 | } | 605 | } |
1001 | else if (gldiscard < 0 && mDesiredDiscardLevel >= 0) | 606 | else if (cur_discard < 0) |
1002 | { | 607 | { |
1003 | // We don't have any data yet, we need something immideately | 608 | // We don't have any data yet, so we don't know the size of the image, treat as 1024x1024 |
1004 | priority = 200000.f; | 609 | // priority = 900000.f; |
610 | static const F64 log_2 = log(2.0); | ||
611 | F32 desired = (F32)(log(1024.0/pixel_priority) / log_2); | ||
612 | S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired + 1; | ||
613 | ddiscard = llclamp(ddiscard, 1, 9); | ||
614 | priority = ddiscard*100000.f; | ||
1005 | } | 615 | } |
1006 | else if (getDiscardLevel() < 0 && mDesiredDiscardLevel < MAX_DISCARD_LEVEL+1) | 616 | else if (cur_discard <= mMinDiscardLevel) |
1007 | { | 617 | { |
1008 | // We have data, but haven't decoded any of it yet, but it on top | 618 | // larger mips are corrupted |
1009 | priority = 300000.f; | 619 | priority = -3.0f; |
1010 | } | 620 | } |
1011 | else if (gldiscard <= mDesiredDiscardLevel) | 621 | else if (cur_discard <= mDesiredDiscardLevel) |
1012 | { | 622 | { |
1013 | priority = 0.0f; | 623 | priority = -4.0f; |
1014 | } | 624 | } |
1015 | else | 625 | else |
1016 | { | 626 | { |
1017 | // priority range = 0 - 10000 (10 ^ 4) | 627 | // priority range = 100000-400000 |
628 | S32 ddiscard = cur_discard - mDesiredDiscardLevel; | ||
1018 | if (getDontDiscard()) | 629 | if (getDontDiscard()) |
1019 | { | 630 | { |
1020 | ddiscard+=2; | 631 | ddiscard+=2; |
1021 | } | 632 | } |
1022 | else if (!getBoundRecently()) | 633 | else if (!getBoundRecently() && mBoostLevel == 0) |
1023 | { | 634 | { |
1024 | ddiscard-=2; | 635 | ddiscard-=2; |
1025 | } | 636 | } |
1026 | else | ||
1027 | { | ||
1028 | ddiscard-=1; | ||
1029 | } | ||
1030 | ddiscard = llclamp(ddiscard, 0, 4); | 637 | ddiscard = llclamp(ddiscard, 0, 4); |
1031 | 638 | priority = ddiscard*100000.f; | |
1032 | priority = powf(10.f,(F32)ddiscard); | ||
1033 | } | 639 | } |
1034 | if (priority > 0.0f) | 640 | if (priority > 0.0f) |
1035 | { | 641 | { |
1036 | F32 pixel_priority = llmin(mMaxVirtualSize * (1.5f + mMaxCosAngle) * (100.f / (1024.f*1024.f)), 100.f); | ||
1037 | pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); | 642 | pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); |
1038 | priority += pixel_priority; | 643 | priority += pixel_priority; |
1039 | if ( mBoostLevel > 0) | 644 | if ( mBoostLevel > BOOST_HIGH) |
1040 | { | 645 | { |
1041 | priority += 1000000.f + 1000.f * mBoostLevel; | 646 | priority += 1000000.f + 1000.f * mBoostLevel; |
1042 | } | 647 | } |
648 | else if ( mBoostLevel > 0) | ||
649 | { | ||
650 | priority += 0.f + 1000.f * mBoostLevel; | ||
651 | } | ||
1043 | } | 652 | } |
1044 | return priority; | 653 | return priority; |
1045 | } | 654 | } |
@@ -1048,7 +657,7 @@ F32 LLViewerImage::calcDecodePriority() | |||
1048 | //static | 657 | //static |
1049 | F32 LLViewerImage::maxDecodePriority() | 658 | F32 LLViewerImage::maxDecodePriority() |
1050 | { | 659 | { |
1051 | return 1400000.f; | 660 | return 2000000.f; |
1052 | } | 661 | } |
1053 | 662 | ||
1054 | void LLViewerImage::setDecodePriority(F32 priority) | 663 | void LLViewerImage::setDecodePriority(F32 priority) |
@@ -1062,10 +671,6 @@ void LLViewerImage::setDecodePriority(F32 priority) | |||
1062 | { | 671 | { |
1063 | mDecodePriority = priority; | 672 | mDecodePriority = priority; |
1064 | } | 673 | } |
1065 | if (mStreamFile) | ||
1066 | { | ||
1067 | mStreamFile->setReadPriority(priority); | ||
1068 | } | ||
1069 | } | 674 | } |
1070 | 675 | ||
1071 | void LLViewerImage::setBoostLevel(S32 level) | 676 | void LLViewerImage::setBoostLevel(S32 level) |
@@ -1079,187 +684,219 @@ void LLViewerImage::setBoostLevel(S32 level) | |||
1079 | 684 | ||
1080 | //============================================================================ | 685 | //============================================================================ |
1081 | 686 | ||
1082 | void LLViewerImage::abortDecode() | 687 | bool LLViewerImage::updateFetch() |
1083 | { | 688 | { |
1084 | // Don't try to recover, just don't set a formatted image. | 689 | mFetchState = 0; |
1085 | // Recovery makes the code MUCH more complex | 690 | mFetchPriority = 0; |
1086 | //llinfos << "Reset on abort decode" << llendl; | 691 | mFetchDeltaTime = 999999.f; |
1087 | resetPacketData(); | 692 | mRequestDeltaTime = 999999.f; |
1088 | 693 | ||
1089 | setFormattedImage(NULL); | 694 | #ifndef LL_RELEASE_FOR_DOWNLOAD |
1090 | mFullyLoaded = FALSE; | 695 | if (mID == gTextureFetch->mDebugID) |
1091 | // Make sure mNeedsAux is false, otherwise it'll try to decode the 5th channel | ||
1092 | mNeedsAux = FALSE; | ||
1093 | mFullWidth = 0; | ||
1094 | mFullHeight = 0; | ||
1095 | setNeedsDecode(FALSE); // Aborting setDecodeData | ||
1096 | mDecodingAux = FALSE; | ||
1097 | if (mStreamFile) | ||
1098 | { | 696 | { |
1099 | llwarns << "Removing bad texture: " << mID << llendl; | 697 | gTextureFetch->mDebugCount++; // for setting breakpoints |
1100 | hoseStreamFile(); | ||
1101 | } | 698 | } |
1102 | else | 699 | #endif |
700 | |||
701 | if (mIsMediaTexture) | ||
1103 | { | 702 | { |
1104 | llwarns << "Removing bad texture: " << mID << llendl; | 703 | llassert_always(!mHasFetcher); |
1105 | LLVFile vf(gVFS, mID, LLAssetType::AT_TEXTURE, LLVFile::READ_WRITE); | 704 | return false; // skip |
1106 | vf.remove(); | ||
1107 | } | 705 | } |
1108 | mRequestedDiscardLevel = -1; // make sure we re-request the data | 706 | if (mNeedsCreateTexture) |
1109 | } | ||
1110 | |||
1111 | void LLViewerImage::hoseStreamFile() | ||
1112 | { | ||
1113 | mStreamFile->remove(); | ||
1114 | |||
1115 | delete mStreamFile; | ||
1116 | mStreamFile = NULL; | ||
1117 | |||
1118 | delete[] mCachedData; | ||
1119 | mCachedData = NULL; | ||
1120 | mCachedSize = 0; | ||
1121 | } | ||
1122 | |||
1123 | |||
1124 | // Sets mStreamFile to NULL when finishes loading. | ||
1125 | BOOL LLViewerImage::loadStreamFile() | ||
1126 | { | ||
1127 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | ||
1128 | // load as much data as possible from the stream cache file | ||
1129 | // TODO: unify stream cache with load local | ||
1130 | |||
1131 | // are we waiting on a file read? | ||
1132 | if (mStreamFile) | ||
1133 | { | 707 | { |
1134 | if (mCachedSize == 0) | 708 | // We may be fetching still (e.g. waiting on write) |
709 | // but don't check until we've processed the raw data we have | ||
710 | return false; | ||
711 | } | ||
712 | if (mFullyLoaded) | ||
713 | { | ||
714 | llassert_always(!mHasFetcher); | ||
715 | return false; | ||
716 | } | ||
717 | if (mIsMissingAsset) | ||
718 | { | ||
719 | llassert_always(!mHasFetcher); | ||
720 | return false; // skip | ||
721 | } | ||
722 | if (!mLoadedCallbackList.empty() && mRawImage.notNull()) | ||
723 | { | ||
724 | return false; // process any raw image data in callbacks before replacing | ||
725 | } | ||
726 | |||
727 | S32 current_discard = getDiscardLevel(); | ||
728 | S32 desired_discard = getDesiredDiscardLevel(); | ||
729 | F32 decode_priority = getDecodePriority(); | ||
730 | |||
731 | if (mIsFetching) | ||
732 | { | ||
733 | // Sets mRawDiscardLevel, mRawImage, mAuxRawImage | ||
734 | S32 fetch_discard = current_discard; | ||
735 | bool finished = gTextureFetch->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage); | ||
736 | if (finished) | ||
1135 | { | 737 | { |
1136 | if (mStreamFile->isLocked(VFSLOCK_APPEND)) | 738 | mIsFetching = FALSE; |
1137 | { | 739 | } |
1138 | // avoid stalling if we are still writing to the file | 740 | else |
1139 | return FALSE; | 741 | { |
1140 | } | 742 | mFetchState = gTextureFetch->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, |
1141 | mCachedSize = mStreamFile->getSize(); | 743 | mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); |
1142 | if (mCachedSize >= 27) | 744 | } |
745 | |||
746 | // We may have data ready regardless of whether or not we are finished (e.g. waiting on write) | ||
747 | if (mRawImage.notNull()) | ||
748 | { | ||
749 | mRawDiscardLevel = fetch_discard; | ||
750 | if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && | ||
751 | (current_discard < 0 || mRawDiscardLevel < current_discard)) | ||
1143 | { | 752 | { |
1144 | mCachedData = new U8[mCachedSize]; | 753 | if (getComponents() != mRawImage->getComponents()) |
1145 | mStreamFile->read(mCachedData, mCachedSize, TRUE, 100 + mDecodePriority); | 754 | { |
755 | // We've changed the number of components, so we need to move any | ||
756 | // objects using this pool to a different pool. | ||
757 | mComponents = mRawImage->getComponents(); | ||
758 | gImageList.dirtyImage(this); | ||
759 | } | ||
760 | mIsRawImageValid = TRUE; | ||
761 | gImageList.mCreateTextureList.insert(this); | ||
762 | mNeedsCreateTexture = TRUE; | ||
763 | mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; | ||
764 | mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; | ||
1146 | } | 765 | } |
1147 | else | 766 | else |
1148 | { | 767 | { |
1149 | llwarns << "Cached image " << mID << " has length " << mCachedSize << " not loading" << llendl; | 768 | // Data is ready but we don't need it |
1150 | 769 | // (received it already while fetcher was writing to disk) | |
1151 | mStreamFile->remove(); | 770 | destroyRawImage(); |
1152 | 771 | return false; // done | |
1153 | delete mStreamFile; | ||
1154 | mStreamFile = NULL; | ||
1155 | mCachedSize = 0; | ||
1156 | return FALSE; | ||
1157 | } | 772 | } |
1158 | } | 773 | } |
1159 | 774 | ||
1160 | // is it finished? | 775 | if (!mIsFetching) |
1161 | if (mStreamFile->isReadComplete()) | ||
1162 | { | 776 | { |
1163 | //llinfos << mID << ": loading from stream file " << llendl; | 777 | if (mRawDiscardLevel < 0) |
1164 | U16 packet; | ||
1165 | U32 file_version; | ||
1166 | LLUUID file_id; | ||
1167 | |||
1168 | U8 *tmp = mCachedData; | ||
1169 | memcpy(&file_version, tmp, 4); | ||
1170 | tmp += 4; | ||
1171 | memcpy(file_id.mData, tmp, 16); | ||
1172 | tmp += 16; | ||
1173 | |||
1174 | if (file_version != sCurrentFileVersion || | ||
1175 | file_id != mID) | ||
1176 | { | ||
1177 | // this file is from an old version, failed to open, or is invalid | ||
1178 | hoseStreamFile(); | ||
1179 | |||
1180 | return TRUE; // done | ||
1181 | } | ||
1182 | |||
1183 | mGotFirstPacket = TRUE; | ||
1184 | |||
1185 | memcpy(&mDataCodec, tmp, 1); | ||
1186 | tmp += 1; | ||
1187 | memcpy(&mPackets, tmp, 2); | ||
1188 | tmp += 2; | ||
1189 | |||
1190 | memcpy(&mTotalBytes, tmp, 4); | ||
1191 | tmp += 4; | ||
1192 | |||
1193 | while (tmp - mCachedData < mCachedSize) | ||
1194 | { | 778 | { |
1195 | memcpy(&packet, tmp, 2); | 779 | // We finished but received no data |
1196 | tmp += 2; | 780 | if (current_discard < 0) |
1197 | |||
1198 | if (packet >= mPackets) | ||
1199 | { | 781 | { |
1200 | llwarns << "Cached image " << mID << " has bogus packet " << packet << " of " << mPackets << llendl; | 782 | llwarns << mID << ": Marking image as missing" << llendl; |
1201 | 783 | setIsMissingAsset(); | |
1202 | hoseStreamFile(); | 784 | desired_discard = -1; |
1203 | return TRUE; // done | ||
1204 | } | ||
1205 | |||
1206 | U16 data_size; | ||
1207 | memcpy((U8*)&(data_size), tmp, 2); | ||
1208 | tmp += 2; | ||
1209 | |||
1210 | if (tmp + data_size > mCachedData + mCachedSize) | ||
1211 | { | ||
1212 | llwarns << "Cached image " << mID << " has bad length " << mCachedSize << ", should be " << (S32)(tmp + data_size - mCachedData) << llendl; | ||
1213 | |||
1214 | hoseStreamFile(); | ||
1215 | return TRUE; // done | ||
1216 | } | ||
1217 | |||
1218 | if (mReceivedPacketMap.find(packet) == mReceivedPacketMap.end()) | ||
1219 | { | ||
1220 | U8 *buf = new U8[data_size]; | ||
1221 | memcpy(buf, tmp, data_size); | ||
1222 | mReceivedPacketMap[packet] = new LLViewerImagePacket(buf, data_size, packet, TRUE); | ||
1223 | } | 785 | } |
1224 | else | 786 | else |
1225 | { | 787 | { |
1226 | // Technically this assertion is correct, but there may be bogus VFS files out there which invalidate this | 788 | llwarns << mID << ": Setting min discard to " << current_discard << llendl; |
1227 | // condition. | 789 | mMinDiscardLevel = current_discard; |
1228 | //llassert(!mReceivedPacketMap[packet]->mWroteToDisk); | 790 | desired_discard = current_discard; |
1229 | mReceivedPacketMap[packet]->mWroteToDisk = TRUE; | ||
1230 | } | 791 | } |
1231 | 792 | destroyRawImage(); | |
1232 | tmp += data_size; | ||
1233 | |||
1234 | mPacketsReceived++; | ||
1235 | } | 793 | } |
794 | else if (mRawImage.isNull()) | ||
795 | { | ||
796 | // We have data, but our fetch failed to return raw data | ||
797 | // *TODO: FIgure out why this is happening and fix it | ||
798 | destroyRawImage(); | ||
799 | } | ||
800 | } | ||
801 | else if (mDecodePriority >= 0.f) | ||
802 | { | ||
803 | gTextureFetch->updateRequestPriority(mID, mDecodePriority); | ||
804 | } | ||
805 | } | ||
1236 | 806 | ||
1237 | delete mStreamFile; | 807 | bool make_request = true; |
1238 | mStreamFile = NULL; | 808 | |
1239 | 809 | if (decode_priority <= 0) | |
1240 | delete[] mCachedData; | 810 | { |
1241 | mCachedData = NULL; | 811 | make_request = false; |
1242 | mCachedSize = 0; | 812 | } |
1243 | 813 | else if (mNeedsCreateTexture || mIsMissingAsset) | |
1244 | // Make sure we process all of the packet data associated with this texture. | 814 | { |
1245 | mLastPacketProcessed = -1; | 815 | make_request = false; |
1246 | mLastPacket = -1; | 816 | } |
1247 | mLastPacketTimer.reset(); | 817 | else if (current_discard >= 0 && current_discard <= mMinDiscardLevel) |
1248 | 818 | { | |
1249 | // Process the packets and write to disk any that have not been written | 819 | make_request = false; |
1250 | checkPacketData(); | 820 | } |
1251 | 821 | else | |
1252 | return TRUE; // done | 822 | { |
823 | if (mIsFetching) | ||
824 | { | ||
825 | if (mRequestedDiscardLevel <= desired_discard) | ||
826 | { | ||
827 | make_request = false; | ||
828 | } | ||
1253 | } | 829 | } |
1254 | else | 830 | else |
1255 | { | 831 | { |
1256 | return FALSE; // still loading | 832 | if (current_discard >= 0 && current_discard <= desired_discard) |
833 | { | ||
834 | make_request = false; | ||
835 | } | ||
1257 | } | 836 | } |
1258 | } | 837 | } |
1259 | else | 838 | |
839 | if (make_request) | ||
840 | { | ||
841 | S32 w=0, h=0, c=0; | ||
842 | if (current_discard >= 0) | ||
843 | { | ||
844 | w = getWidth(0); | ||
845 | h = getHeight(0); | ||
846 | c = getComponents(); | ||
847 | } | ||
848 | if (!mDontDiscard) | ||
849 | { | ||
850 | if (mBoostLevel == 0) | ||
851 | { | ||
852 | desired_discard = llmax(desired_discard, current_discard-1); | ||
853 | } | ||
854 | else | ||
855 | { | ||
856 | desired_discard = llmax(desired_discard, current_discard-2); | ||
857 | } | ||
858 | } | ||
859 | if (gTextureFetch->createRequest(getID(),getTargetHost(), decode_priority, | ||
860 | w, h, c, desired_discard, | ||
861 | needsAux())) | ||
862 | { | ||
863 | mHasFetcher = TRUE; | ||
864 | mIsFetching = TRUE; | ||
865 | mRequestedDiscardLevel = desired_discard; | ||
866 | mFetchState = gTextureFetch->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, | ||
867 | mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); | ||
868 | } | ||
869 | // if createRequest() failed, we're finishing up a request for this UUID, | ||
870 | // wait for it to complete | ||
871 | } | ||
872 | else if (mHasFetcher && !mIsFetching) | ||
873 | { | ||
874 | // Only delete requests that haven't receeived any network data for a while | ||
875 | const F32 FETCH_IDLE_TIME = 5.f; | ||
876 | if (mLastPacketTimer.getElapsedTimeF32() > FETCH_IDLE_TIME) | ||
877 | { | ||
878 | // llinfos << "Deleting request: " << getID() << " Discard: " << current_discard << " <= min:" << mMinDiscardLevel << " or priority == 0: " << decode_priority << llendl; | ||
879 | gTextureFetch->deleteRequest(getID(), true); | ||
880 | mHasFetcher = FALSE; | ||
881 | } | ||
882 | } | ||
883 | |||
884 | llassert_always(mRawImage.notNull() || (!mNeedsCreateTexture && !mIsRawImageValid)); | ||
885 | |||
886 | return mIsFetching ? true : false; | ||
887 | } | ||
888 | |||
889 | void LLViewerImage::setIsMissingAsset() | ||
890 | { | ||
891 | if (mHasFetcher) | ||
1260 | { | 892 | { |
1261 | return TRUE; // not loading | 893 | gTextureFetch->deleteRequest(getID(), true); |
894 | mHasFetcher = FALSE; | ||
895 | mIsFetching = FALSE; | ||
896 | mFetchState = 0; | ||
897 | mFetchPriority = 0; | ||
1262 | } | 898 | } |
899 | mIsMissingAsset = TRUE; | ||
1263 | } | 900 | } |
1264 | 901 | ||
1265 | //============================================================================ | 902 | //============================================================================ |
@@ -1278,13 +915,14 @@ void LLViewerImage::setLoadedCallback( loaded_callback_func loaded_callback, S32 | |||
1278 | mLoadedCallbackList.push_back(entryp); | 915 | mLoadedCallbackList.push_back(entryp); |
1279 | } | 916 | } |
1280 | 917 | ||
1281 | void LLViewerImage::doLoadedCallbacks() | 918 | bool LLViewerImage::doLoadedCallbacks() |
1282 | { | 919 | { |
1283 | // Need to make sure we don't do these during the process of a decode or something? | 920 | if (mNeedsCreateTexture) |
1284 | if ((mFormattedImagep.notNull() && mFormattedImagep->isDecoding()) || mNeedsCreateTexture) | ||
1285 | { | 921 | { |
1286 | return; | 922 | return false; |
1287 | } | 923 | } |
924 | |||
925 | bool res = false; | ||
1288 | 926 | ||
1289 | if (isMissingAsset()) | 927 | if (isMissingAsset()) |
1290 | { | 928 | { |
@@ -1311,12 +949,6 @@ void LLViewerImage::doLoadedCallbacks() | |||
1311 | gl_discard = MAX_DISCARD_LEVEL + 1; | 949 | gl_discard = MAX_DISCARD_LEVEL + 1; |
1312 | } | 950 | } |
1313 | 951 | ||
1314 | // assert: We should either have a valid raw image, be decoding one, or not have one at all | ||
1315 | llassert(mIsRawImageValid || needsDecode() || mRawImage.isNull()); | ||
1316 | // assert: We should either not have a raw image, or it's discard level should be <= gl_discard | ||
1317 | llassert(!mIsRawImageValid || mRawDiscardLevel <= gl_discard); | ||
1318 | |||
1319 | |||
1320 | // | 952 | // |
1321 | // Determine the quality levels of textures that we can provide to callbacks | 953 | // Determine the quality levels of textures that we can provide to callbacks |
1322 | // and whether we need to do decompression/readback to get it | 954 | // and whether we need to do decompression/readback to get it |
@@ -1335,18 +967,9 @@ void LLViewerImage::doLoadedCallbacks() | |||
1335 | } | 967 | } |
1336 | else | 968 | else |
1337 | { | 969 | { |
1338 | if (mFormattedImagep.notNull()) | 970 | // We have no data at all, we need to get it |
1339 | { | 971 | // Do this by forcing the best aux discard to be 0. |
1340 | // If we don't have a raw image or a GL image, we need to decode from a formatted image | 972 | best_aux_discard = 0; |
1341 | best_aux_discard = llmin(best_aux_discard, | ||
1342 | mFormattedImagep->calcDiscardLevelBytes(mFormattedImagep->getDataSize())); | ||
1343 | } | ||
1344 | else | ||
1345 | { | ||
1346 | // We have no data at all, we need to get the formatted image. | ||
1347 | // Do this by forcing the best aux discard to be 0. | ||
1348 | best_aux_discard = 0; | ||
1349 | } | ||
1350 | } | 973 | } |
1351 | 974 | ||
1352 | 975 | ||
@@ -1357,7 +980,6 @@ void LLViewerImage::doLoadedCallbacks() | |||
1357 | bool run_gl_callbacks = false; | 980 | bool run_gl_callbacks = false; |
1358 | bool run_raw_callbacks = false; | 981 | bool run_raw_callbacks = false; |
1359 | bool need_readback = false; | 982 | bool need_readback = false; |
1360 | bool need_decompress = false; | ||
1361 | 983 | ||
1362 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); | 984 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); |
1363 | iter != mLoadedCallbackList.end(); ) | 985 | iter != mLoadedCallbackList.end(); ) |
@@ -1375,12 +997,6 @@ void LLViewerImage::doLoadedCallbacks() | |||
1375 | // We have useful data, run the callbacks | 997 | // We have useful data, run the callbacks |
1376 | run_raw_callbacks = true; | 998 | run_raw_callbacks = true; |
1377 | } | 999 | } |
1378 | else if (entryp->mLastUsedDiscard > best_aux_discard) | ||
1379 | { | ||
1380 | // We need to decompress data, but don't need | ||
1381 | // to run the callbacks | ||
1382 | need_decompress = true; | ||
1383 | } | ||
1384 | } | 1000 | } |
1385 | else | 1001 | else |
1386 | { | 1002 | { |
@@ -1420,10 +1036,8 @@ void LLViewerImage::doLoadedCallbacks() | |||
1420 | createRawImage(gl_discard, TRUE); | 1036 | createRawImage(gl_discard, TRUE); |
1421 | readBackRaw(gl_discard, mRawImage); | 1037 | readBackRaw(gl_discard, mRawImage); |
1422 | mIsRawImageValid = TRUE; | 1038 | mIsRawImageValid = TRUE; |
1423 | } | 1039 | llassert_always(mRawImage.notNull()); |
1424 | if (need_decompress) | 1040 | llassert_always(!mNeedsAux || mAuxRawImage.notNull()); |
1425 | { | ||
1426 | startImageDecode(); | ||
1427 | } | 1041 | } |
1428 | 1042 | ||
1429 | // | 1043 | // |
@@ -1434,10 +1048,6 @@ void LLViewerImage::doLoadedCallbacks() | |||
1434 | // Do callbacks which require raw image data. | 1048 | // Do callbacks which require raw image data. |
1435 | //llinfos << "doLoadedCallbacks raw for " << getID() << llendl; | 1049 | //llinfos << "doLoadedCallbacks raw for " << getID() << llendl; |
1436 | 1050 | ||
1437 | LLImageRaw* raw_image = mRawImage; | ||
1438 | LLImageRaw* raw_image_aux = mAuxRawImage; | ||
1439 | llassert(!mNeedsAux || mAuxRawImage.notNull()); | ||
1440 | |||
1441 | // Call each party interested in the raw data. | 1051 | // Call each party interested in the raw data. |
1442 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); | 1052 | for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); |
1443 | iter != mLoadedCallbackList.end(); ) | 1053 | iter != mLoadedCallbackList.end(); ) |
@@ -1450,28 +1060,26 @@ void LLViewerImage::doLoadedCallbacks() | |||
1450 | // to satisfy the interested party, then this is the last time that | 1060 | // to satisfy the interested party, then this is the last time that |
1451 | // we're going to call them. | 1061 | // we're going to call them. |
1452 | 1062 | ||
1063 | llassert_always(mRawImage.notNull()); | ||
1064 | llassert_always(!mNeedsAux || mAuxRawImage.notNull()); | ||
1453 | 1065 | ||
1454 | BOOL final = mRawDiscardLevel <= entryp->mDesiredDiscard ? TRUE : FALSE; | 1066 | BOOL final = mRawDiscardLevel <= entryp->mDesiredDiscard ? TRUE : FALSE; |
1455 | //llinfos << "Running callback for " << getID() << llendl; | 1067 | //llinfos << "Running callback for " << getID() << llendl; |
1456 | //llinfos << raw_image->getWidth() << "x" << raw_image->getHeight() << llendl; | 1068 | //llinfos << mRawImage->getWidth() << "x" << mRawImage->getHeight() << llendl; |
1457 | if (final) | 1069 | if (final) |
1458 | { | 1070 | { |
1459 | //llinfos << "Final!" << llendl; | 1071 | //llinfos << "Final!" << llendl; |
1460 | } | 1072 | } |
1461 | entryp->mLastUsedDiscard = mRawDiscardLevel; | 1073 | entryp->mLastUsedDiscard = mRawDiscardLevel; |
1462 | entryp->mCallback(TRUE, this, raw_image, raw_image_aux, mRawDiscardLevel, final, entryp->mUserData); | 1074 | entryp->mCallback(TRUE, this, mRawImage, mAuxRawImage, mRawDiscardLevel, final, entryp->mUserData); |
1463 | if (final) | 1075 | if (final) |
1464 | { | 1076 | { |
1465 | iter = mLoadedCallbackList.erase(curiter); | 1077 | iter = mLoadedCallbackList.erase(curiter); |
1466 | delete entryp; | 1078 | delete entryp; |
1467 | } | 1079 | } |
1080 | res = true; | ||
1468 | } | 1081 | } |
1469 | } | 1082 | } |
1470 | |||
1471 | // | ||
1472 | // If you want to keep a copy of the raw image, you better copy it off yourself | ||
1473 | // | ||
1474 | destroyRawImage(); | ||
1475 | } | 1083 | } |
1476 | 1084 | ||
1477 | // | 1085 | // |
@@ -1497,6 +1105,7 @@ void LLViewerImage::doLoadedCallbacks() | |||
1497 | iter = mLoadedCallbackList.erase(curiter); | 1105 | iter = mLoadedCallbackList.erase(curiter); |
1498 | delete entryp; | 1106 | delete entryp; |
1499 | } | 1107 | } |
1108 | res = true; | ||
1500 | } | 1109 | } |
1501 | } | 1110 | } |
1502 | } | 1111 | } |
@@ -1508,450 +1117,14 @@ void LLViewerImage::doLoadedCallbacks() | |||
1508 | { | 1117 | { |
1509 | gImageList.mCallbackList.erase(this); | 1118 | gImageList.mCallbackList.erase(this); |
1510 | } | 1119 | } |
1511 | } | ||
1512 | 1120 | ||
1513 | //============================================================================ | 1121 | // Done with any raw image data at this point (will be re-created if we still have callbacks) |
1514 | 1122 | destroyRawImage(); | |
1515 | // static | ||
1516 | void LLViewerImage::receiveImage(LLMessageSystem *msg, void **user_data) | ||
1517 | { | ||
1518 | LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); | ||
1519 | |||
1520 | // Receive image header, copy into image object and decompresses | ||
1521 | // if this is a one-packet image. | ||
1522 | |||
1523 | LLUUID id; | ||
1524 | |||
1525 | char ip_string[256]; | ||
1526 | u32_to_ip_string(msg->getSenderIP(),ip_string); | ||
1527 | |||
1528 | if (msg->getReceiveCompressedSize()) | ||
1529 | { | ||
1530 | gImageList.sTextureBits += msg->getReceiveCompressedSize() * 8; | ||
1531 | } | ||
1532 | else | ||
1533 | { | ||
1534 | gImageList.sTextureBits += msg->getReceiveSize() * 8; | ||
1535 | } | ||
1536 | gImageList.sTexturePackets++; | ||
1537 | |||
1538 | msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); | ||
1539 | |||
1540 | char id_string[UUID_STR_LENGTH]; | ||
1541 | id.toString(id_string); | ||
1542 | |||
1543 | LLViewerImage *image = gImageList.getImage(id); // Look up the correct image | ||
1544 | |||
1545 | image->mLastPacketTimer.reset(); | ||
1546 | 1123 | ||
1547 | if (image->mFullyLoaded) | 1124 | return res; |
1548 | { | ||
1549 | // llinfos << id << ":" << " Packet 0 for already loaded image!" << llendl; | ||
1550 | return; | ||
1551 | } | ||
1552 | |||
1553 | // check to see if we've gotten this packet before | ||
1554 | if (image->mGotFirstPacket) | ||
1555 | { | ||
1556 | //llinfos << id << ":" << " Duplicate Packet 0" << llendl; | ||
1557 | return; | ||
1558 | } | ||
1559 | if (!image->mRequested) | ||
1560 | { | ||
1561 | // llinfos << id << ":" << " Packet 0 for unrequested image!" << llendl; | ||
1562 | return; | ||
1563 | } | ||
1564 | |||
1565 | image->mGotFirstPacket = TRUE; | ||
1566 | |||
1567 | // Copy header data into image object | ||
1568 | msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, image->mDataCodec); | ||
1569 | msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, image->mPackets); | ||
1570 | msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, image->mTotalBytes); | ||
1571 | |||
1572 | if (0 == image->mPackets) | ||
1573 | { | ||
1574 | llwarns << "Img: " << (gTextureTable.getName(id).empty() ? id_string : gTextureTable.getName(id)) << ":" << " Number of packets is 0" << llendl; | ||
1575 | return; | ||
1576 | } | ||
1577 | |||
1578 | lldebugst(LLERR_IMAGE) << "Img: " << (gTextureTable.getName(id).empty() ? id_string : gTextureTable.getName(id)) << ":" << " Packet 0:" << image->mPackets - 1 << llendl; | ||
1579 | |||
1580 | U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); | ||
1581 | |||
1582 | // Got a packet, reset the counter. | ||
1583 | image->mRequestTime.reset(); | ||
1584 | image->mPacketsReceived++; | ||
1585 | |||
1586 | if (data_size) | ||
1587 | { | ||
1588 | if (gVFS->getExists(image->mID, LLAssetType::AT_TEXTURE)) | ||
1589 | { | ||
1590 | // We have data in the VFS, but it's not loaded. | ||
1591 | // We should start the VFS load on the assumption that we're going to use this data shortly. | ||
1592 | image->startVFSLoad(); | ||
1593 | // We can throw out this data, because we have at least ONE packet on the disk | ||
1594 | //llinfos << "Throwing out first packet for " << image->mID << " which we already have a VFS file for!" << llendl; | ||
1595 | return; | ||
1596 | } | ||
1597 | |||
1598 | // this buffer gets saved off in the packet list | ||
1599 | U8 *data = new U8[data_size]; | ||
1600 | msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); | ||
1601 | |||
1602 | // output this image data to cache file | ||
1603 | // only do this if we don't have old data | ||
1604 | LLVFile file(gVFS, image->mID, LLAssetType::AT_TEXTURE, LLVFile::APPEND); | ||
1605 | if (! file.getSize()) | ||
1606 | { | ||
1607 | BOOL write_to_vfs = FALSE; | ||
1608 | if (image->mPackets == 0) | ||
1609 | { | ||
1610 | // Uh oh, we don't have packet data size | ||
1611 | // This must be something from a local cache | ||
1612 | llwarns << "Creating VFS file even though we don't know the number of packets!" << llendl; | ||
1613 | } | ||
1614 | else | ||
1615 | { | ||
1616 | write_to_vfs = file.setMaxSize(image->mTotalBytes + IMAGE_HEADER_SIZE + image->mPackets * PACKET_HEADER_SIZE); | ||
1617 | } | ||
1618 | |||
1619 | // to avoid another dynamic allocation, just assume we won't be gettimg image packets > 1 MTU | ||
1620 | if (data_size > MTUBYTES) | ||
1621 | { | ||
1622 | llerrs << "image data chunk too large: " << data_size << " bytes" << llendl; | ||
1623 | } | ||
1624 | |||
1625 | if (write_to_vfs) | ||
1626 | { | ||
1627 | const S32 WRITE_BUF_SIZE = IMAGE_HEADER_SIZE + PACKET_HEADER_SIZE + MTUBYTES; | ||
1628 | U8 buffer[WRITE_BUF_SIZE]; | ||
1629 | U8 *tmp = buffer; | ||
1630 | |||
1631 | // write current version byte to file, so we can change the format and detect old files later | ||
1632 | memcpy(tmp, &LLViewerImage::sCurrentFileVersion, 4); | ||
1633 | tmp += 4; | ||
1634 | memcpy(tmp, id.mData, 16); | ||
1635 | tmp += 16; | ||
1636 | memcpy(tmp, (U8*)&(image->mDataCodec), 1); | ||
1637 | tmp += 1; | ||
1638 | memcpy(tmp, (U8*)&(image->mPackets), 2); | ||
1639 | tmp += 2; | ||
1640 | memcpy(tmp, (U8*)&(image->mTotalBytes), 4); | ||
1641 | tmp += 4; | ||
1642 | U16 zero = 0; | ||
1643 | memcpy(tmp, (U8*)&zero, 2); | ||
1644 | tmp += 2; | ||
1645 | memcpy(tmp, (U8*)&(data_size), 2); | ||
1646 | tmp += 2; | ||
1647 | |||
1648 | // now copy in the image data | ||
1649 | // it's a shame we can't use the original data buffer | ||
1650 | // but this needs to be a single, atomic write | ||
1651 | memcpy(tmp, data, data_size); | ||
1652 | |||
1653 | // llinfos << "Writing packet 0 to disk for " << image->getID() << llendl; | ||
1654 | file.write(buffer, IMAGE_HEADER_SIZE + PACKET_HEADER_SIZE + data_size); | ||
1655 | } | ||
1656 | // do this AFTER writing to the VFS since LLViewerImagePacket may delete 'data' | ||
1657 | llassert( image->mReceivedPacketMap.find(0) == image->mReceivedPacketMap.end() ); | ||
1658 | image->mReceivedPacketMap[0] = new LLViewerImagePacket(data, data_size, 0, TRUE); | ||
1659 | } | ||
1660 | |||
1661 | image->checkPacketData(); | ||
1662 | } | ||
1663 | } | ||
1664 | |||
1665 | |||
1666 | /////////////////////////////////////////////////////////////////////////////// | ||
1667 | // TODO: lastbytes vs. texturebits? | ||
1668 | // TODO: is mRequested already used? | ||
1669 | |||
1670 | // static | ||
1671 | void LLViewerImage::receiveImagePacket(LLMessageSystem *msg, void **user_data) | ||
1672 | { | ||
1673 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | ||
1674 | LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); | ||
1675 | |||
1676 | // Receives image packet, copy into image object, | ||
1677 | // checks if all packets received, decompresses if so. | ||
1678 | |||
1679 | LLUUID id; | ||
1680 | U16 packet_num; | ||
1681 | char id_string[UUID_STR_LENGTH]; | ||
1682 | |||
1683 | char ip_string[256]; | ||
1684 | u32_to_ip_string(msg->getSenderIP(),ip_string); | ||
1685 | |||
1686 | if (msg->getReceiveCompressedSize()) | ||
1687 | { | ||
1688 | gImageList.sTextureBits += msg->getReceiveCompressedSize() * 8; | ||
1689 | } | ||
1690 | else | ||
1691 | { | ||
1692 | gImageList.sTextureBits += msg->getReceiveSize() * 8; | ||
1693 | } | ||
1694 | gImageList.sTexturePackets++; | ||
1695 | |||
1696 | //llprintline("Start decode, image header..."); | ||
1697 | msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); | ||
1698 | msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num); | ||
1699 | |||
1700 | id.toString(id_string); | ||
1701 | |||
1702 | LLViewerImage *image = gImageList.hasImage(id); // Look up the correct image | ||
1703 | if (!image ||!(image->mRequested)) | ||
1704 | { | ||
1705 | // Getting a packet for an unrequested image. | ||
1706 | lldebugst(LLERR_IMAGE) << "Img: " << (gTextureTable.getName(id).empty() ? id_string : gTextureTable.getName(id)) << " Packet "; | ||
1707 | llcont << packet_num << " for unrequested from " << ip_string << llendl; | ||
1708 | |||
1709 | // don't cancel the request - this might just be an out of order packet | ||
1710 | return; | ||
1711 | } | ||
1712 | |||
1713 | image->mLastPacketTimer.reset(); | ||
1714 | |||
1715 | if (image->mReceivedPacketMap.find(packet_num) != image->mReceivedPacketMap.end()) | ||
1716 | { | ||
1717 | return; | ||
1718 | } | ||
1719 | |||
1720 | // check to see if we already got this packet | ||
1721 | BOOL duplicate = FALSE; | ||
1722 | if (packet_num <= image->mLastPacketProcessed) | ||
1723 | { | ||
1724 | duplicate = TRUE; | ||
1725 | } | ||
1726 | else if (image->mReceivedPacketMap.find(packet_num) != image->mReceivedPacketMap.end()) | ||
1727 | { | ||
1728 | duplicate = TRUE; | ||
1729 | } | ||
1730 | |||
1731 | if (duplicate) | ||
1732 | { | ||
1733 | //llinfos << image->mID << ": duplicate packet " << packet_num << " last " << image->mLastPacketProcessed << llendl; | ||
1734 | return; | ||
1735 | } | ||
1736 | |||
1737 | // Got a packet, reset the counter. | ||
1738 | image->mRequestTime.reset(); | ||
1739 | image->mPacketsReceived++; | ||
1740 | |||
1741 | std::string tex_name = gTextureTable.getName(id); | ||
1742 | if (image->mPackets == 0) | ||
1743 | { | ||
1744 | lldebugst(LLERR_IMAGE) << "Img: " << (tex_name.empty() ? id_string : tex_name) << " Packet " << packet_num << " out of order " << llendl; | ||
1745 | } | ||
1746 | else | ||
1747 | { | ||
1748 | lldebugst(LLERR_IMAGE) << "Img: " << (tex_name.empty() ? id_string : tex_name) << " Packet " << packet_num << ":" << image->mPackets - 1 << llendl; | ||
1749 | } | ||
1750 | |||
1751 | |||
1752 | U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); | ||
1753 | if (data_size) | ||
1754 | { | ||
1755 | |||
1756 | U8 *data = new U8[data_size]; | ||
1757 | msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); | ||
1758 | |||
1759 | // as above assume we won't be gettimg image packets > 1 MTU | ||
1760 | if (data_size > MTUBYTES) | ||
1761 | { | ||
1762 | llerrs << "image data chunk too large: " << data_size << " bytes" << llendl; | ||
1763 | } | ||
1764 | |||
1765 | // We don't want to write it to disk yet, just put it on the queue. | ||
1766 | image->mReceivedPacketMap[packet_num] = new LLViewerImagePacket(data, data_size, packet_num, FALSE); | ||
1767 | // Process this packet. | ||
1768 | image->checkPacketData(); | ||
1769 | } | ||
1770 | } | ||
1771 | |||
1772 | |||
1773 | BOOL LLViewerImage::checkPacketData() | ||
1774 | { | ||
1775 | LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); | ||
1776 | |||
1777 | S32 cur_size = 0; | ||
1778 | S32 next_size = 0; | ||
1779 | S32 next_discard = llmax(getDiscardLevel()-1,0); | ||
1780 | |||
1781 | // 1. Check if we already have formatted data to decode | ||
1782 | if (mFormattedImagep.notNull()) | ||
1783 | { | ||
1784 | if ((mFormattedImagep->isDecoding()) || mStreamFile) | ||
1785 | { | ||
1786 | return TRUE; // we're busy decoding, don't request more data yet | ||
1787 | } | ||
1788 | |||
1789 | cur_size = mFormattedImagep->getDataSize(); | ||
1790 | next_size = mFormattedImagep->calcDataSize(next_discard); | ||
1791 | next_size = llmin(next_size, (S32)mTotalBytes); | ||
1792 | |||
1793 | if (cur_size >= next_size) | ||
1794 | { | ||
1795 | setDecodeData(mFormattedImagep->getData(), cur_size); | ||
1796 | return TRUE; | ||
1797 | } | ||
1798 | } | ||
1799 | |||
1800 | if (mFullyLoaded) | ||
1801 | { | ||
1802 | // Somehow we think we have new packet data but are flagged as fully loaded | ||
1803 | resetPacketData(); | ||
1804 | return TRUE; | ||
1805 | } | ||
1806 | |||
1807 | // 2. Check if we already have new packets | ||
1808 | if (!mPackets || !mGotFirstPacket) | ||
1809 | { | ||
1810 | return FALSE; | ||
1811 | } | ||
1812 | if (mReceivedPacketMap.empty() || mReceivedPacketMap.find(mLastPacketProcessed + 1) == mReceivedPacketMap.end()) | ||
1813 | { | ||
1814 | return FALSE; | ||
1815 | } | ||
1816 | |||
1817 | // 3. Decide if we have enough new data to decode | ||
1818 | S32 new_size = cur_size; | ||
1819 | U16 next_packet_num = mLastPacketProcessed; | ||
1820 | for (vip_map_t::iterator iter = mReceivedPacketMap.begin(); | ||
1821 | iter != mReceivedPacketMap.end() && iter->second->mPacketNum == ++next_packet_num; | ||
1822 | iter++) | ||
1823 | { | ||
1824 | new_size += iter->second->mDataSize; | ||
1825 | mLastPacket = iter->second->mPacketNum; | ||
1826 | } | ||
1827 | mLastBytesProcessed = new_size; | ||
1828 | |||
1829 | if (new_size < next_size) | ||
1830 | { | ||
1831 | return FALSE; | ||
1832 | } | ||
1833 | |||
1834 | if (!gVFS->getExists(mID, LLAssetType::AT_TEXTURE)) | ||
1835 | { | ||
1836 | // We must have removed the file, probably because it was corrupted. Abort! | ||
1837 | //llinfos << "Reset on no VFS file!" << llendl; | ||
1838 | abortDecode(); | ||
1839 | return FALSE; | ||
1840 | } | ||
1841 | |||
1842 | if (mLastPacketProcessed == -1 && cur_size != 0) | ||
1843 | { | ||
1844 | llerrs << "LLViewerImage: duplicate first packet!" << llendl; | ||
1845 | } | ||
1846 | |||
1847 | // 4. Append new data to existing data and decode | ||
1848 | |||
1849 | // 4a. Write the packets to disk | ||
1850 | LLVFile file(gVFS, mID, LLAssetType::AT_TEXTURE, LLVFile::APPEND); | ||
1851 | vip_map_t::iterator first_iter = mReceivedPacketMap.begin(); | ||
1852 | S32 first_idx = 0; | ||
1853 | while (first_iter != mReceivedPacketMap.end()) | ||
1854 | { | ||
1855 | S32 packet_data_size = 0; | ||
1856 | vip_map_t::iterator end_iter = first_iter; | ||
1857 | S32 end_idx = first_idx; | ||
1858 | while(end_iter != mReceivedPacketMap.end()) | ||
1859 | { | ||
1860 | LLViewerImagePacket *vip = end_iter->second; | ||
1861 | if (!vip->mWroteToDisk) | ||
1862 | { | ||
1863 | packet_data_size += (PACKET_HEADER_SIZE + vip->mDataSize); | ||
1864 | } | ||
1865 | else | ||
1866 | { | ||
1867 | break; | ||
1868 | } | ||
1869 | ++end_iter; | ||
1870 | ++end_idx; | ||
1871 | } | ||
1872 | if (packet_data_size > 0) | ||
1873 | { | ||
1874 | U8* packet_data_buffer = new U8[packet_data_size]; | ||
1875 | U8* packet_data = packet_data_buffer; | ||
1876 | for (vip_map_t::iterator iter = first_iter; iter != end_iter; ++iter) | ||
1877 | { | ||
1878 | LLViewerImagePacket *vip = iter->second; | ||
1879 | |||
1880 | memcpy(packet_data, &vip->mPacketNum, 2); | ||
1881 | memcpy(packet_data + 2, &(vip->mDataSize), 2); | ||
1882 | memcpy(packet_data + PACKET_HEADER_SIZE, vip->mData, vip->mDataSize); | ||
1883 | packet_data += (PACKET_HEADER_SIZE + vip->mDataSize); | ||
1884 | |||
1885 | vip->mWroteToDisk = TRUE; | ||
1886 | } | ||
1887 | if (packet_data - packet_data_buffer != packet_data_size) llerrs << "wtf?" << llendl; | ||
1888 | |||
1889 | // llinfos << mID << " Writing packets " << first_idx << "-" << end_idx << " to file." << llendl; | ||
1890 | file.write(packet_data_buffer, packet_data_size); | ||
1891 | |||
1892 | delete[] packet_data_buffer; | ||
1893 | } | ||
1894 | if (end_iter == first_iter) | ||
1895 | { | ||
1896 | ++end_iter; | ||
1897 | ++end_idx; | ||
1898 | } | ||
1899 | first_iter = end_iter; | ||
1900 | first_idx = end_idx; | ||
1901 | } | ||
1902 | |||
1903 | // 4b. Append the data | ||
1904 | U8* data = new U8[new_size]; | ||
1905 | if (cur_size > 0) | ||
1906 | { | ||
1907 | memcpy(data, mFormattedImagep->getData(), cur_size); | ||
1908 | } | ||
1909 | LLViewerImagePacket *pkt = mReceivedPacketMap.begin()->second; | ||
1910 | while (pkt && pkt->mPacketNum == mLastPacketProcessed + 1) | ||
1911 | { | ||
1912 | memcpy((U8*)(data + cur_size), pkt->mData, pkt->mDataSize); | ||
1913 | cur_size += pkt->mDataSize; | ||
1914 | mLastPacketProcessed = pkt->mPacketNum; | ||
1915 | delete mReceivedPacketMap.begin()->second; | ||
1916 | mReceivedPacketMap.erase(mReceivedPacketMap.begin()); | ||
1917 | pkt = NULL; | ||
1918 | if (!mReceivedPacketMap.empty()) | ||
1919 | { | ||
1920 | pkt = mReceivedPacketMap.begin()->second; | ||
1921 | } | ||
1922 | } | ||
1923 | |||
1924 | llassert(cur_size == new_size); | ||
1925 | lldebugst(LLERR_IMAGE) << "IMAGE RECEIVED: " << mID.getString() << " Bytes: " << cur_size << "/" << mTotalBytes << llendl; | ||
1926 | |||
1927 | // 4c. Set the data to be decoded, and the number of bytes to use. | ||
1928 | setDecodeData(data, new_size); | ||
1929 | |||
1930 | // 5. Recalculate the image priority | ||
1931 | gImageList.removeImageFromList(this); | ||
1932 | F32 decode_priority = calcDecodePriority(); | ||
1933 | setDecodePriority(decode_priority); | ||
1934 | gImageList.addImageToList(this); | ||
1935 | |||
1936 | return TRUE; | ||
1937 | } | 1125 | } |
1938 | 1126 | ||
1939 | F32 LLViewerImage::getDecodeProgress(F32 *data_progress_p) | 1127 | //============================================================================ |
1940 | { | ||
1941 | F32 decode_progress = 0.0f; | ||
1942 | F32 data_progress = 0.0f; | ||
1943 | |||
1944 | if (mLastPacket >= 0) | ||
1945 | { | ||
1946 | S32 max_bytes = mTotalBytes; | ||
1947 | S32 data_bytes = mLastBytesProcessed; | ||
1948 | S32 decode_bytes = mFormattedImagep.notNull() ? mFormattedImagep->getDataSize() : data_bytes; | ||
1949 | data_progress = (F32)data_bytes / (F32)max_bytes; | ||
1950 | decode_progress = (F32)decode_bytes / (F32)max_bytes; | ||
1951 | } | ||
1952 | if (data_progress_p) *data_progress_p = data_progress; | ||
1953 | return decode_progress; | ||
1954 | } | ||
1955 | 1128 | ||
1956 | // Call with 0,0 to turn this feature off. | 1129 | // Call with 0,0 to turn this feature off. |
1957 | void LLViewerImage::setKnownDrawSize(S32 width, S32 height) | 1130 | void LLViewerImage::setKnownDrawSize(S32 width, S32 height) |
@@ -1976,13 +1149,8 @@ BOOL LLViewerImage::bind(S32 stage) const | |||
1976 | BOOL res = bindTextureInternal(stage); | 1149 | BOOL res = bindTextureInternal(stage); |
1977 | if (res) | 1150 | if (res) |
1978 | { | 1151 | { |
1979 | if (mIsMissingAsset) | 1152 | //llassert_always(mIsMissingAsset == FALSE); |
1980 | { | 1153 | |
1981 | // If we can bind, clearly we have an asset. | ||
1982 | // If mIsMissingAsset was true and we get here, it's likely | ||
1983 | // that the asset server was messed up and then it recovered. | ||
1984 | mIsMissingAsset = FALSE; | ||
1985 | } | ||
1986 | } | 1154 | } |
1987 | else | 1155 | else |
1988 | { | 1156 | { |
@@ -2013,7 +1181,6 @@ BOOL LLViewerImage::bind(S32 stage) const | |||
2013 | LLImageRaw* LLViewerImage::createRawImage(S8 discard_level, BOOL allocate) | 1181 | LLImageRaw* LLViewerImage::createRawImage(S8 discard_level, BOOL allocate) |
2014 | { | 1182 | { |
2015 | llassert(discard_level >= 0); | 1183 | llassert(discard_level >= 0); |
2016 | llassert(mFormattedImagep.isNull() || !mFormattedImagep->isDecoding()); | ||
2017 | if (mRawImage.notNull()) | 1184 | if (mRawImage.notNull()) |
2018 | { | 1185 | { |
2019 | llerrs << "createRawImage() called with existing mRawImage" << llendl; | 1186 | llerrs << "createRawImage() called with existing mRawImage" << llendl; |