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