llviewerimage.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llviewerimage.h"
00035 
00036 // Library includes
00037 #include "imageids.h"
00038 #include "llmath.h"
00039 #include "llerror.h"
00040 #include "llgl.h"
00041 #include "llglheaders.h"
00042 #include "llhost.h"
00043 #include "llimage.h"
00044 #include "llimagebmp.h"
00045 #include "llimagej2c.h"
00046 #include "llimagetga.h"
00047 #include "llmemtype.h"
00048 #include "llstl.h"
00049 #include "llvfile.h"
00050 #include "llvfs.h"
00051 #include "message.h"
00052 #include "lltimer.h"
00053 
00054 // viewer includes
00055 #include "lldrawpool.h"
00056 #include "lltexturefetch.h"
00057 #include "llviewerimagelist.h"
00058 #include "llviewercontrol.h"
00059 #include "pipeline.h"
00060 #include "viewer.h"
00061 
00063 
00064 // statics
00065 LLPointer<LLViewerImage> LLViewerImage::sMissingAssetImagep = NULL;
00066 LLPointer<LLViewerImage> LLViewerImage::sWhiteImagep = NULL;
00067 LLPointer<LLImageGL> LLViewerImage::sDefaultImagep = NULL;
00068 LLPointer<LLViewerImage> LLViewerImage::sSmokeImagep = NULL;
00069 LLPointer<LLImageGL> LLViewerImage::sNullImagep = NULL;
00070 
00071 S32 LLViewerImage::sImageCount = 0;
00072 LLTimer LLViewerImage::sEvaluationTimer;
00073 F32 LLViewerImage::sDesiredDiscardBias = 0.f;
00074 static F32 sDesiredDiscardBiasMin = -2.0f; // -max number of levels to improve image quality by
00075 static F32 sDesiredDiscardBiasMax = 1.5f; // max number of levels to reduce image quality by
00076 F32 LLViewerImage::sDesiredDiscardScale = 1.1f;
00077 S32 LLViewerImage::sBoundTextureMemory = 0;
00078 S32 LLViewerImage::sTotalTextureMemory = 0;
00079 S32 LLViewerImage::sMaxBoundTextureMem = 0;
00080 S32 LLViewerImage::sMaxTotalTextureMem = 0;
00081 BOOL LLViewerImage::sDontLoadVolumeTextures = FALSE;
00082 
00083 // static
00084 void LLViewerImage::initClass()
00085 {
00086         sNullImagep = new LLImageGL(1,1,3,TRUE);
00087         LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,3);
00088         raw->clear(0x77, 0x77, 0x77, 0xFF);
00089         sNullImagep->createGLTexture(0, raw);
00090 
00091 #if 1
00092         LLViewerImage* imagep = new LLViewerImage(IMG_DEFAULT, TRUE);
00093         sDefaultImagep = imagep;
00094         const S32 dim = 128;
00095         LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
00096         U8* data = image_raw->getData();
00097         for (S32 i = 0; i<dim; i++)
00098         {
00099                 for (S32 j = 0; j<dim; j++)
00100                 {
00101 #if 0
00102                         const S32 border = 2;
00103                         if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
00104                         {
00105                                 *data++ = 0xff;
00106                                 *data++ = 0xff;
00107                                 *data++ = 0xff;
00108                         }
00109                         else
00110 #endif
00111                         {
00112                                 *data++ = 0x7f;
00113                                 *data++ = 0x7f;
00114                                 *data++ = 0x7f;
00115                         }
00116                 }
00117         }
00118         imagep->createGLTexture(0, image_raw);
00119         image_raw = NULL;
00120         gImageList.addImage(imagep);
00121         imagep->dontDiscard();
00122 #else
00123         sDefaultImagep = gImageList.getImage(IMG_DEFAULT, TRUE, TRUE);
00124 #endif
00125         sSmokeImagep = gImageList.getImage(IMG_SMOKE, TRUE, TRUE);
00126 
00127 }
00128 
00129 // static
00130 void LLViewerImage::cleanupClass()
00131 {
00132         stop_glerror();
00133         sNullImagep = NULL;
00134         sDefaultImagep = NULL;
00135         sSmokeImagep = NULL;
00136         sMissingAssetImagep = NULL;
00137         sWhiteImagep = NULL;
00138 }
00139 
00140 // tuning params
00141 const F32 discard_bias_delta = .05f;
00142 const F32 discard_delta_time = 0.5f;
00143 const S32 min_non_tex_system_mem = (128<<20); // 128 MB
00144 // non-const (used externally
00145 F32 texmem_lower_bound_scale = 0.85f;
00146 F32 texmem_middle_bound_scale = 0.925f;
00147 
00148 //static
00149 void LLViewerImage::updateClass(const F32 velocity, const F32 angular_velocity)
00150 {
00151         sBoundTextureMemory = LLImageGL::sBoundTextureMemory;
00152         sTotalTextureMemory = LLImageGL::sGlobalTextureMemory;
00153         sMaxBoundTextureMem = gImageList.getMaxResidentTexMem();
00154         
00155         sMaxTotalTextureMem = sMaxBoundTextureMem * 2;
00156         if (sMaxBoundTextureMem > 64000000)
00157         {
00158                 sMaxTotalTextureMem -= sMaxBoundTextureMem/4;
00159         }
00160         
00161         if ((U32)sMaxTotalTextureMem > gSysMemory.getPhysicalMemoryClamped() - (U32)min_non_tex_system_mem)
00162         {
00163                 sMaxTotalTextureMem = (S32)gSysMemory.getPhysicalMemoryClamped() - min_non_tex_system_mem;
00164         }
00165         
00166         if (sBoundTextureMemory >= sMaxBoundTextureMem ||
00167                 sTotalTextureMemory >= sMaxTotalTextureMem)
00168         {
00169                 // If we are using more texture memory than we should,
00170                 // scale up the desired discard level
00171                 if (sEvaluationTimer.getElapsedTimeF32() > discard_delta_time)
00172                 {
00173                         sDesiredDiscardBias += discard_bias_delta;
00174                         sEvaluationTimer.reset();
00175                 }
00176         }
00177         else if (sDesiredDiscardBias > 0.0f &&
00178                          sBoundTextureMemory < sMaxBoundTextureMem*texmem_lower_bound_scale &&
00179                          sTotalTextureMemory < sMaxTotalTextureMem*texmem_lower_bound_scale)
00180         {
00181                 // If we are using less texture memory than we should,
00182                 // scale down the desired discard level
00183                 if (sEvaluationTimer.getElapsedTimeF32() > discard_delta_time)
00184                 {
00185                         sDesiredDiscardBias -= discard_bias_delta;
00186                         sEvaluationTimer.reset();
00187                 }
00188         }
00189         sDesiredDiscardBias = llclamp(sDesiredDiscardBias, sDesiredDiscardBiasMin, sDesiredDiscardBiasMax);
00190 }
00191 
00192 //----------------------------------------------------------------------------
00193 
00194 const U32 LLViewerImage::sCurrentFileVersion = 1;
00195 
00196 LLViewerImage::LLViewerImage(const LLUUID& id, BOOL usemipmaps)
00197         : LLImageGL(usemipmaps),
00198           mID(id)
00199 {
00200         init(true);
00201         sImageCount++;
00202 }
00203 
00204 LLViewerImage::LLViewerImage(const U32 width, const U32 height, const U8 components, BOOL usemipmaps)
00205         : LLImageGL(width, height, components, usemipmaps)
00206 {
00207         init(true);
00208         mNeedsAux = FALSE;
00209         // Create an empty image of the specified size and width
00210         mID.generate();
00211         mFullyLoaded = TRUE;
00212         sImageCount++;
00213 }
00214 
00215 LLViewerImage::LLViewerImage(const LLImageRaw* raw, BOOL usemipmaps)
00216         : LLImageGL(raw, usemipmaps)
00217 {
00218         init(true);
00219         mNeedsAux = FALSE;
00220         // Create an empty image of the specified size and width
00221         mID.generate();
00222         mFullyLoaded = TRUE;
00223         sImageCount++;
00224 }
00225 
00226 void LLViewerImage::init(bool firstinit)
00227 {
00228         mFullWidth = 0;
00229         mFullHeight = 0;
00230         mNeedsAux = FALSE;
00231         mTexelsPerImage = 64.f*64.f;
00232         mMaxVirtualSize = 0.f;
00233         mDiscardVirtualSize = 0.f;
00234         mMaxCosAngle = -1.f;
00235         mRequestedDiscardLevel = -1;
00236         mRequestedDownloadPriority = 0.f;
00237         mFullyLoaded = FALSE;
00238         mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
00239         mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
00240         mCalculatedDiscardLevel = -1.f;
00241 
00242         mDecodingAux = FALSE;
00243 
00244         mKnownDrawWidth = 0;
00245         mKnownDrawHeight = 0;
00246 
00247         if (firstinit)
00248         {
00249                 mDecodePriority = 0.f;
00250                 mInImageList = 0;
00251         }
00252         mIsMediaTexture = FALSE;
00253 
00254         mBoostLevel = LLViewerImage::BOOST_NONE;
00255         
00256         // Only set mIsMissingAsset true when we know for certain that the database
00257         // does not contain this image.
00258         mIsMissingAsset = FALSE;
00259 
00260         mNeedsCreateTexture = FALSE;
00261         
00262         mIsRawImageValid = FALSE;
00263         mRawDiscardLevel = INVALID_DISCARD_LEVEL;
00264         mMinDiscardLevel = 0;
00265 
00266         mTargetHost = LLHost::invalid;
00267 
00268         mHasFetcher = FALSE;
00269         mIsFetching = FALSE;
00270         mFetchState = 0;
00271         mFetchPriority = 0;
00272         mDownloadProgress = 0.f;
00273         mFetchDeltaTime = 999999.f;
00274         mDecodeFrame = 0;
00275         mVisibleFrame = 0;
00276 }
00277 
00278 // virtual
00279 void LLViewerImage::dump()
00280 {
00281         LLImageGL::dump();
00282 
00283         llinfos << "LLViewerImage"
00284                         << " mID " << mID
00285                         << " mIsMissingAsset " << (S32)mIsMissingAsset
00286                         << " mFullWidth " << mFullWidth
00287                         << " mFullHeight " << mFullHeight
00288                         << llendl;
00289 }
00290 
00292 
00293 LLViewerImage::~LLViewerImage()
00294 {
00295         if (mHasFetcher)
00296         {
00297                 gTextureFetch->deleteRequest(getID(), true);
00298         }
00299         // Explicitly call LLViewerImage::cleanup since we're in a destructor and cleanup is virtual
00300         LLViewerImage::cleanup();
00301         sImageCount--;
00302 }
00303 
00304 
00306 
00307 void LLViewerImage::cleanup()
00308 {
00309         for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
00310                 iter != mLoadedCallbackList.end(); )
00311         {
00312                 LLLoadedCallbackEntry *entryp = *iter++;
00313                 // We never finished loading the image.  Indicate failure.
00314                 // Note: this allows mLoadedCallbackUserData to be cleaned up.
00315                 entryp->mCallback( FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData );
00316                 delete entryp;
00317         }
00318         mLoadedCallbackList.clear();
00319 
00320         // Clean up image data
00321         destroyRawImage();
00322         
00323         // LLImageGL::cleanup will get called more than once when this is used in the destructor.
00324         LLImageGL::cleanup();
00325 }
00326 
00327 void LLViewerImage::reinit(BOOL usemipmaps /* = TRUE */)
00328 {
00329         LLViewerImage::cleanup();
00330         LLImageGL::init(usemipmaps);
00331         init(false);
00332         setSize(0,0,0);
00333 }
00334 
00336 
00337 // ONLY called from LLViewerImageList
00338 BOOL LLViewerImage::createTexture(S32 usename/*= 0*/)
00339 {
00340         if (!mNeedsCreateTexture)
00341         {
00342                 destroyRawImage();
00343                 return FALSE;
00344         }
00345         mNeedsCreateTexture     = FALSE;
00346         if (mRawImage.isNull())
00347         {
00348                 llerrs << "LLViewerImage trying to create texture with no Raw Image" << llendl;
00349         }
00350 //      llinfos << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ",
00351 //                                              mRawDiscardLevel, 
00352 //                                              mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize())
00353 //                      << mID.getString() << llendl;
00354         BOOL res = TRUE;
00355         if (!gNoRender)
00356         {
00357                 if (LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight()))
00358                 {
00359                         res = LLImageGL::createGLTexture(mRawDiscardLevel, mRawImage, usename);
00360                 }
00361                 else
00362                 {
00363                         // A non power-of-two image was uploaded (through a non standard client)
00364                         // We treat these images as missing assets which causes them to
00365                         // be renderd as 'missing image' and to stop requesting data
00366                         setIsMissingAsset();
00367                         destroyRawImage();
00368                         return FALSE;
00369                 }
00370         }
00371 
00372         //
00373         // Iterate through the list of image loading callbacks to see
00374         // what sort of data they need.
00375         //
00376         // *TODO: Fix image callback code
00377         BOOL imageraw_callbacks = FALSE;
00378         for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
00379                 iter != mLoadedCallbackList.end(); )
00380         {
00381                 LLLoadedCallbackEntry *entryp = *iter++;
00382                 if (entryp->mNeedsImageRaw)
00383                 {
00384                         imageraw_callbacks = TRUE;
00385                         break;
00386                 }
00387         }
00388 
00389         if (!imageraw_callbacks)
00390         {
00391                 destroyRawImage();
00392         }
00393         return res;
00394 }
00395 
00396 //============================================================================
00397 
00398 void LLViewerImage::addTextureStats(F32 pixel_area,
00399                                                                     F32 texel_area_ratio, // = 1.0
00400                                                                     F32 cos_center_angle) const // = 1.0
00401 {
00402         F32 virtual_size = pixel_area / texel_area_ratio;
00403         if (virtual_size > mMaxVirtualSize)
00404         {
00405                 mMaxVirtualSize = virtual_size;
00406         }
00407         cos_center_angle = llclamp(cos_center_angle, -1.f, 1.f);
00408         if (cos_center_angle > mMaxCosAngle)
00409         {
00410                 mMaxCosAngle = cos_center_angle;
00411         }
00412 }
00413 
00414 void LLViewerImage::resetTextureStats(BOOL zero)
00415 {
00416         if (zero)
00417         {
00418                 mMaxVirtualSize = 0.0f;
00419                 mMaxCosAngle = -1.0f;
00420         }
00421         else
00422         {
00423                 mMaxVirtualSize -= mMaxVirtualSize * .10f; // decay by 5%/update
00424                 mMaxCosAngle = -1.0f;
00425         }
00426 }
00427 
00428 // This is gauranteed to get called periodically for every texture
00429 void LLViewerImage::processTextureStats()
00430 {
00431         // Generate the request priority and render priority
00432         if (mDontDiscard || !getUseMipMaps())
00433         {
00434                 mDesiredDiscardLevel = 0;
00435                 if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
00436                         mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
00437         }
00438         else if (mBoostLevel < LLViewerImage::BOOST_HIGH && mMaxVirtualSize <= 10.f)
00439         {
00440                 // If the image has not been significantly visible in a while, we don't want it
00441                 mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1));
00442         }
00443         else if ((!mFullWidth && !mWidth)  || (!mFullHeight && !mHeight))
00444         {
00445                 mDesiredDiscardLevel =  mMaxDiscardLevel;
00446         }
00447         else
00448         {
00449                 //static const F64 log_2 = log(2.0);
00450                 static const F64 log_4 = log(4.0);
00451 
00452                 S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT);
00453                 S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT);
00454                 mTexelsPerImage = (F32)fullwidth * fullheight;
00455 
00456                 F32 discard_level = 0.f;
00457 
00458                 // If we know the output width and height, we can force the discard
00459                 // level to the correct value, and thus not decode more texture
00460                 // data than we need to.
00461                 if (mBoostLevel == LLViewerImage::BOOST_UI ||
00462                         mBoostLevel == LLViewerImage::BOOST_PREVIEW ||
00463                         mBoostLevel == LLViewerImage::BOOST_AVATAR_SELF)        // JAMESDEBUG what about AVATAR_BAKED_SELF?
00464                 {
00465                         discard_level = 0; // full res
00466                 }
00467                 else if (mKnownDrawWidth && mKnownDrawHeight)
00468                 {
00469                         S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight;
00470 
00471                         // Use log_4 because we're in square-pixel space, so an image
00472                         // with twice the width and twice the height will have mTexelsPerImage
00473                         // 4 * draw_size
00474                         discard_level = (F32)(log(mTexelsPerImage/draw_texels) / log_4);
00475                 }
00476                 else
00477                 {
00478                         if ((mCalculatedDiscardLevel >= 0.f) &&
00479                                 (llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f))
00480                         {
00481                                 // < 20% change in virtual size = no change in desired discard
00482                                 discard_level = mCalculatedDiscardLevel; 
00483                         }
00484                         else
00485                         {
00486                                 // Calculate the required scale factor of the image using pixels per texel
00487                                 discard_level = (F32)(log(mTexelsPerImage/mMaxVirtualSize) / log_4);
00488                                 mDiscardVirtualSize = mMaxVirtualSize;
00489                                 mCalculatedDiscardLevel = discard_level;
00490                         }
00491                 }
00492                 if (mBoostLevel < LLViewerImage::BOOST_HIGH)
00493                 {
00494                         static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load!
00495                         discard_level += discard_bias;
00496                         discard_level += sDesiredDiscardBias;
00497                         discard_level *= sDesiredDiscardScale; // scale
00498                 }
00499                 discard_level = floorf(discard_level);
00500 //              discard_level -= (gImageList.mVideoMemorySetting>>1); // more video ram = higher detail
00501 
00502                 F32 min_discard = 0.f;
00503                 if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
00504                         min_discard = 1.f; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
00505 
00506                 discard_level = llclamp(discard_level, min_discard, (F32)MAX_DISCARD_LEVEL);
00507                 
00508                 // Can't go higher than the max discard level
00509                 mDesiredDiscardLevel = llmin((S32)mMaxDiscardLevel+1, (S32)discard_level);
00510                 // Clamp to min desired discard
00511                 mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, mDesiredDiscardLevel);
00512 
00513                 //
00514                 // At this point we've calculated the quality level that we want,
00515                 // if possible.  Now we check to see if we have it, and take the
00516                 // proper action if we don't.
00517                 //
00518 
00519                 BOOL increase_discard = FALSE;
00520                 S32 current_discard = getDiscardLevel();
00521                 if ((sDesiredDiscardBias > 0.0f) &&
00522                         (current_discard >= 0 && mDesiredDiscardLevel >= current_discard))
00523                 {
00524                         if ( sBoundTextureMemory > sMaxBoundTextureMem*texmem_middle_bound_scale)
00525                         {
00526                                 // Limit the amount of GL memory bound each frame
00527                                 if (mDesiredDiscardLevel > current_discard)
00528                                 {
00529                                         increase_discard = TRUE;
00530                                 }
00531                         }
00532                         if ( sTotalTextureMemory > sMaxTotalTextureMem*texmem_middle_bound_scale)
00533                         {
00534                                 // Only allow GL to have 2x the video card memory
00535                                 if (!getBoundRecently())
00536                                 {
00537                                         increase_discard = TRUE;
00538                                 }
00539                         }
00540                         if (increase_discard)
00541                         {
00542                                 //                      llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl;
00543                                 sBoundTextureMemory -= mTextureMemory;
00544                                 sTotalTextureMemory -= mTextureMemory;
00545                                 // Increase the discard level (reduce the texture res)
00546                                 S32 new_discard = current_discard+1;
00547                                 setDiscardLevel(new_discard);
00548                                 sBoundTextureMemory += mTextureMemory;
00549                                 sTotalTextureMemory += mTextureMemory;
00550                         }
00551                 }
00552         }
00553 }
00554 
00555 //============================================================================
00556 
00557 F32 LLViewerImage::calcDecodePriority()
00558 {
00559 #ifndef LL_RELEASE_FOR_DOWNLOAD
00560         if (mID == gTextureFetch->mDebugID)
00561         {
00562                 gTextureFetch->mDebugCount++; // for setting breakpoints
00563         }
00564 #endif
00565         
00566         if (mNeedsCreateTexture)
00567         {
00568                 return mDecodePriority; // no change while waiting to create
00569         }
00570 
00571         F32 priority;
00572         S32 cur_discard = getDiscardLevel();
00573         F32 pixel_priority = fsqrtf(mMaxVirtualSize) * (1.f + mMaxCosAngle);
00574         const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame
00575         mDecodeFrame++;
00576         if (pixel_priority > 0.f)
00577         {
00578                 mVisibleFrame = mDecodeFrame;
00579         }
00580         
00581         if (mIsMissingAsset)
00582         {
00583                 priority = 0.0f;
00584         }
00585         else if (mDesiredDiscardLevel > mMaxDiscardLevel)
00586         {
00587                 // Don't decode anything we don't need
00588                 priority = -1.0f;
00589         }
00590         else if (pixel_priority <= 0.f && (cur_discard < 0 || mDesiredDiscardLevel < cur_discard))
00591         {
00592                 // Not on screen but we might want some data
00593                 if (mBoostLevel > BOOST_HIGH)
00594                 {
00595                         // Always want high boosted images
00596                         priority = 1.f;
00597                 }
00598                 else if (mVisibleFrame == 0 || (mDecodeFrame - mVisibleFrame > MIN_NOT_VISIBLE_FRAMES))
00599                 {
00600                         // Don't decode anything that isn't visible unless it's important
00601                         priority = -2.0f;
00602                 }
00603                 else
00604                 {
00605                         // Leave the priority as-is
00606                         return mDecodePriority;
00607                 }
00608         }
00609         else if (cur_discard < 0)
00610         {
00611                 // We don't have any data yet, so we don't know the size of the image, treat as 1024x1024
00612 //              priority = 900000.f;
00613                 static const F64 log_2 = log(2.0);
00614                 F32 desired = (F32)(log(1024.0/pixel_priority) / log_2);
00615                 S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired + 1;
00616                 ddiscard = llclamp(ddiscard, 1, 9);
00617                 priority = ddiscard*100000.f;
00618         }
00619         else if (cur_discard <= mMinDiscardLevel)
00620         {
00621                 // larger mips are corrupted
00622                 priority = -3.0f;
00623         }
00624         else if (cur_discard <= mDesiredDiscardLevel)
00625         {
00626                 priority = -4.0f;
00627         }
00628         else
00629         {
00630                 // priority range = 100000-400000
00631                 S32 ddiscard = cur_discard - mDesiredDiscardLevel;
00632                 if (getDontDiscard())
00633                 {
00634                         ddiscard+=2;
00635                 }
00636                 else if (!getBoundRecently() && mBoostLevel == 0)
00637                 {
00638                         ddiscard-=2;
00639                 }
00640                 ddiscard = llclamp(ddiscard, 0, 4);
00641                 priority = ddiscard*100000.f;
00642         }
00643         if (priority > 0.0f)
00644         {
00645                 pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f);
00646                 priority += pixel_priority;
00647                 if ( mBoostLevel > BOOST_HIGH)
00648                 {
00649                         priority += 1000000.f + 1000.f * mBoostLevel;
00650                 }
00651                 else if ( mBoostLevel > 0)
00652                 {
00653                         priority +=       0.f + 1000.f * mBoostLevel;
00654                 }
00655         }
00656         return priority;
00657 }
00658 
00659 // A value >= max value calculated above for normalization
00660 //static
00661 F32 LLViewerImage::maxDecodePriority()
00662 {
00663         return 2000000.f;
00664 }
00665 
00666 void LLViewerImage::setDecodePriority(F32 priority)
00667 {
00668         llassert(!mInImageList);
00669         if (priority < 0.0f)
00670         {
00671                 mDecodePriority = calcDecodePriority();
00672         }
00673         else
00674         {
00675                 mDecodePriority = priority;
00676         }
00677 }
00678 
00679 void LLViewerImage::setBoostLevel(S32 level)
00680 {
00681         mBoostLevel = level;
00682         if (level >= LLViewerImage::BOOST_HIGH)
00683         {
00684                 processTextureStats();
00685         }
00686 }
00687 
00688 //============================================================================
00689 
00690 bool LLViewerImage::updateFetch()
00691 {
00692         mFetchState = 0;
00693         mFetchPriority = 0;
00694         mFetchDeltaTime = 999999.f;
00695         mRequestDeltaTime = 999999.f;
00696 
00697 #ifndef LL_RELEASE_FOR_DOWNLOAD
00698         if (mID == gTextureFetch->mDebugID)
00699         {
00700                 gTextureFetch->mDebugCount++; // for setting breakpoints
00701         }
00702 #endif
00703         
00704         if (mIsMediaTexture)
00705         {
00706                 llassert_always(!mHasFetcher);
00707                 return false; // skip
00708         }
00709         if (mNeedsCreateTexture)
00710         {
00711                 // We may be fetching still (e.g. waiting on write)
00712                 // but don't check until we've processed the raw data we have
00713                 return false;
00714         }
00715         if (mFullyLoaded)
00716         {
00717                 llassert_always(!mHasFetcher);
00718                 return false;
00719         }
00720         if (mIsMissingAsset)
00721         {
00722                 llassert_always(!mHasFetcher);
00723                 return false; // skip
00724         }
00725         if (!mLoadedCallbackList.empty() && mRawImage.notNull())
00726         {
00727                 return false; // process any raw image data in callbacks before replacing
00728         }
00729         
00730         S32 current_discard = getDiscardLevel();
00731         S32 desired_discard = getDesiredDiscardLevel();
00732         F32 decode_priority = getDecodePriority();
00733         
00734         if (mIsFetching)
00735         {
00736                 // Sets mRawDiscardLevel, mRawImage, mAuxRawImage
00737                 S32 fetch_discard = current_discard;
00738                 bool finished = gTextureFetch->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage);
00739                 if (finished)
00740                 {
00741                         mIsFetching = FALSE;
00742                 }
00743                 else
00744                 {
00745                         mFetchState = gTextureFetch->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
00746                                                                                                            mFetchPriority, mFetchDeltaTime, mRequestDeltaTime);
00747                 }
00748                 
00749                 // We may have data ready regardless of whether or not we are finished (e.g. waiting on write)
00750                 if (mRawImage.notNull())
00751                 {
00752                         mRawDiscardLevel = fetch_discard;
00753                         if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) &&
00754                                 (current_discard < 0 || mRawDiscardLevel < current_discard))
00755                         {
00756                                 if (getComponents() != mRawImage->getComponents())
00757                                 {
00758                                         // We've changed the number of components, so we need to move any
00759                                         // objects using this pool to a different pool.
00760                                         mComponents = mRawImage->getComponents();
00761                                         gImageList.dirtyImage(this);
00762                                 }                       
00763                                 mIsRawImageValid = TRUE;
00764                                 gImageList.mCreateTextureList.insert(this);
00765                                 mNeedsCreateTexture = TRUE;
00766                                 mFullWidth = mRawImage->getWidth() << mRawDiscardLevel;
00767                                 mFullHeight = mRawImage->getHeight() << mRawDiscardLevel;
00768                         }
00769                         else
00770                         {
00771                                 // Data is ready but we don't need it
00772                                 // (received it already while fetcher was writing to disk)
00773                                 destroyRawImage();
00774                                 return false; // done
00775                         }
00776                 }
00777                 
00778                 if (!mIsFetching)
00779                 {
00780                         if (mRawDiscardLevel < 0)
00781                         {
00782                                 // We finished but received no data
00783                                 if (current_discard < 0)
00784                                 {
00785                                         llwarns << mID << ": Marking image as missing" << llendl;
00786                                         setIsMissingAsset();
00787                                         desired_discard = -1;
00788                                 }
00789                                 else
00790                                 {
00791                                         llwarns << mID << ": Setting min discard to " << current_discard << llendl;
00792                                         mMinDiscardLevel = current_discard;
00793                                         desired_discard = current_discard;
00794                                 }
00795                                 destroyRawImage();
00796                         }
00797                         else if (mRawImage.isNull())
00798                         {
00799                                 // We have data, but our fetch failed to return raw data
00800                                 // *TODO: FIgure out why this is happening and fix it
00801                                 destroyRawImage();
00802                         }
00803                 }
00804                 else if (mDecodePriority >= 0.f)
00805                 {
00806                         gTextureFetch->updateRequestPriority(mID, mDecodePriority);
00807                 }
00808         }
00809 
00810         bool make_request = true;
00811         
00812         if (decode_priority <= 0)
00813         {
00814                 make_request = false;
00815         }
00816         else if (mNeedsCreateTexture || mIsMissingAsset)
00817         {
00818                 make_request = false;
00819         }
00820         else if (current_discard >= 0 && current_discard <= mMinDiscardLevel)
00821         {
00822                 make_request = false;
00823         }
00824         else
00825         {
00826                 if (mIsFetching)
00827                 {
00828                         if (mRequestedDiscardLevel <= desired_discard)
00829                         {
00830                                 make_request = false;
00831                         }
00832                 }
00833                 else
00834                 {
00835                         if (current_discard >= 0 && current_discard <= desired_discard)
00836                         {
00837                                 make_request = false;
00838                         }
00839                 }
00840         }
00841         
00842         if (make_request)
00843         {
00844                 S32 w=0, h=0, c=0;
00845                 if (current_discard >= 0)
00846                 {
00847                         w = getWidth(0);
00848                         h = getHeight(0);
00849                         c = getComponents();
00850                 }
00851                 if (!mDontDiscard)
00852                 {
00853                         if (mBoostLevel == 0)
00854                         {
00855                                 desired_discard = llmax(desired_discard, current_discard-1);
00856                         }
00857                         else
00858                         {
00859                                 desired_discard = llmax(desired_discard, current_discard-2);
00860                         }
00861                 }
00862                 if (gTextureFetch->createRequest(getID(),getTargetHost(), decode_priority,
00863                                                                                  w, h, c, desired_discard,
00864                                                                                  needsAux()))
00865                 {
00866                         mHasFetcher = TRUE;
00867                         mIsFetching = TRUE;
00868                         mRequestedDiscardLevel = desired_discard;
00869                         mFetchState = gTextureFetch->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
00870                                                                                                            mFetchPriority, mFetchDeltaTime, mRequestDeltaTime);
00871                 }
00872                 // if createRequest() failed, we're finishing up a request for this UUID,
00873                 // wait for it to complete
00874         }
00875         else if (mHasFetcher && !mIsFetching)
00876         {
00877                 // Only delete requests that haven't receeived any network data for a while
00878                 const F32 FETCH_IDLE_TIME = 5.f;
00879                 if (mLastPacketTimer.getElapsedTimeF32() > FETCH_IDLE_TIME)
00880                 {
00881 //                      llinfos << "Deleting request: " << getID() << " Discard: " << current_discard << " <= min:" << mMinDiscardLevel << " or priority == 0: " << decode_priority << llendl;
00882                         gTextureFetch->deleteRequest(getID(), true);
00883                         mHasFetcher = FALSE;
00884                 }
00885         }
00886         
00887         llassert_always(mRawImage.notNull() || (!mNeedsCreateTexture && !mIsRawImageValid));
00888         
00889         return mIsFetching ? true : false;
00890 }
00891 
00892 void LLViewerImage::setIsMissingAsset()
00893 {
00894         if (mHasFetcher)
00895         {
00896                 gTextureFetch->deleteRequest(getID(), true);
00897                 mHasFetcher = FALSE;
00898                 mIsFetching = FALSE;
00899                 mFetchState = 0;
00900                 mFetchPriority = 0;
00901         }
00902         mIsMissingAsset = TRUE;
00903 }
00904 
00905 //============================================================================
00906 
00907 void LLViewerImage::setLoadedCallback( loaded_callback_func loaded_callback, S32 discard_level, BOOL keep_imageraw, void* userdata)
00908 {
00909         //
00910         // Don't do ANYTHING here, just add it to the global callback list
00911         //
00912         if (mLoadedCallbackList.empty())
00913         {
00914                 // Put in list to call this->doLoadedCallbacks() periodically
00915                 gImageList.mCallbackList.insert(this);
00916         }
00917         LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata);
00918         mLoadedCallbackList.push_back(entryp);
00919 }
00920 
00921 bool LLViewerImage::doLoadedCallbacks()
00922 {
00923         if (mNeedsCreateTexture)
00924         {
00925                 return false;
00926         }
00927 
00928         bool res = false;
00929         
00930         if (isMissingAsset())
00931         {
00932                 for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
00933                         iter != mLoadedCallbackList.end(); )
00934                 {
00935                         LLLoadedCallbackEntry *entryp = *iter++;
00936                         // We never finished loading the image.  Indicate failure.
00937                         // Note: this allows mLoadedCallbackUserData to be cleaned up.
00938                         entryp->mCallback(FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData);
00939                         delete entryp;
00940                 }
00941                 mLoadedCallbackList.clear();
00942 
00943                 // Remove ourself from the global list of textures with callbacks
00944                 gImageList.mCallbackList.erase(this);
00945         }
00946 
00947         S32 gl_discard = getDiscardLevel();
00948 
00949         // If we don't have a legit GL image, set it to be lower than the worst discard level
00950         if (gl_discard == -1)
00951         {
00952                 gl_discard = MAX_DISCARD_LEVEL + 1;
00953         }
00954 
00955         //
00956         // Determine the quality levels of textures that we can provide to callbacks
00957         // and whether we need to do decompression/readback to get it
00958         //
00959         S32 current_raw_discard = MAX_DISCARD_LEVEL + 1; // We can always do a readback to get a raw discard
00960         S32 best_raw_discard = gl_discard;      // Current GL quality level
00961         S32 current_aux_discard = MAX_DISCARD_LEVEL + 1;
00962         S32 best_aux_discard = MAX_DISCARD_LEVEL + 1;
00963 
00964         if (mIsRawImageValid)
00965         {
00966                 // If we have an existing raw image, we have a baseline for the raw and auxiliary quality levels.
00967                 best_raw_discard = llmin(best_raw_discard, mRawDiscardLevel);
00968                 best_aux_discard = llmin(best_aux_discard, mRawDiscardLevel); // We always decode the aux when we decode the base raw
00969                 current_aux_discard = llmin(current_aux_discard, best_aux_discard);
00970         }
00971         else
00972         {
00973                 // We have no data at all, we need to get it
00974                 // Do this by forcing the best aux discard to be 0.
00975                 best_aux_discard = 0;
00976         }
00977 
00978 
00979         //
00980         // See if any of the callbacks would actually run using the data that we can provide,
00981         // and also determine if we need to perform any readbacks or decodes.
00982         //
00983         bool run_gl_callbacks = false;
00984         bool run_raw_callbacks = false;
00985         bool need_readback = false;
00986 
00987         for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
00988                 iter != mLoadedCallbackList.end(); )
00989         {
00990                 LLLoadedCallbackEntry *entryp = *iter++;
00991                 if (entryp->mNeedsImageRaw)
00992                 {
00993                         if (mNeedsAux)
00994                         {
00995                                 //
00996                                 // Need raw and auxiliary channels
00997                                 //
00998                                 if (entryp->mLastUsedDiscard > current_aux_discard)
00999                                 {
01000                                         // We have useful data, run the callbacks
01001                                         run_raw_callbacks = true;
01002                                 }
01003                         }
01004                         else
01005                         {
01006                                 if (entryp->mLastUsedDiscard > current_raw_discard)
01007                                 {
01008                                         // We have useful data, just run the callbacks
01009                                         run_raw_callbacks = true;
01010                                 }
01011                                 else if (entryp->mLastUsedDiscard > best_raw_discard)
01012                                 {
01013                                         // We can readback data, and then run the callbacks
01014                                         need_readback = true;
01015                                         run_raw_callbacks = true;
01016                                 }
01017                         }
01018                 }
01019                 else
01020                 {
01021                         // Needs just GL
01022                         if (entryp->mLastUsedDiscard > gl_discard)
01023                         {
01024                                 // We have enough data, run this callback requiring GL data
01025                                 run_gl_callbacks = true;
01026                         }
01027                 }
01028         }
01029 
01030         //
01031         // Do a readback if required, OR start off a texture decode
01032         //
01033         if (need_readback && (mMaxDiscardLevel > gl_discard))
01034         {
01035                 // Do a readback to get the GL data into the raw image
01036                 // We have GL data.
01037 
01038                 destroyRawImage();
01039                 createRawImage(gl_discard, TRUE);
01040                 readBackRaw(gl_discard, mRawImage, false);
01041                 mIsRawImageValid = TRUE;
01042                 llassert_always(mRawImage.notNull());
01043                 llassert_always(!mNeedsAux || mAuxRawImage.notNull());
01044         }
01045 
01046         //
01047         // Run raw/auxiliary data callbacks
01048         //
01049         if (run_raw_callbacks && mIsRawImageValid && (mRawDiscardLevel <= mMaxDiscardLevel))
01050         {
01051                 // Do callbacks which require raw image data.
01052                 //llinfos << "doLoadedCallbacks raw for " << getID() << llendl;
01053 
01054                 // Call each party interested in the raw data.
01055                 for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
01056                         iter != mLoadedCallbackList.end(); )
01057                 {
01058                         callback_list_t::iterator curiter = iter++;
01059                         LLLoadedCallbackEntry *entryp = *curiter;
01060                         if (entryp->mNeedsImageRaw && (entryp->mLastUsedDiscard > mRawDiscardLevel))
01061                         {
01062                                 // If we've loaded all the data there is to load or we've loaded enough
01063                                 // to satisfy the interested party, then this is the last time that
01064                                 // we're going to call them.
01065 
01066                                 llassert_always(mRawImage.notNull());
01067                                 if(mNeedsAux && mAuxRawImage.isNull())
01068                                 {
01069                                         llwarns << "Raw Image with no Aux Data for callback" << llendl;
01070                                 }
01071                                 BOOL final = mRawDiscardLevel <= entryp->mDesiredDiscard ? TRUE : FALSE;
01072                                 //llinfos << "Running callback for " << getID() << llendl;
01073                                 //llinfos << mRawImage->getWidth() << "x" << mRawImage->getHeight() << llendl;
01074                                 if (final)
01075                                 {
01076                                         //llinfos << "Final!" << llendl;
01077                                 }
01078                                 entryp->mLastUsedDiscard = mRawDiscardLevel;
01079                                 entryp->mCallback(TRUE, this, mRawImage, mAuxRawImage, mRawDiscardLevel, final, entryp->mUserData);
01080                                 if (final)
01081                                 {
01082                                         iter = mLoadedCallbackList.erase(curiter);
01083                                         delete entryp;
01084                                 }
01085                                 res = true;
01086                         }
01087                 }
01088         }
01089 
01090         //
01091         // Run GL callbacks
01092         //
01093         if (run_gl_callbacks && (gl_discard <= mMaxDiscardLevel))
01094         {
01095                 //llinfos << "doLoadedCallbacks GL for " << getID() << llendl;
01096 
01097                 // Call the callbacks interested in GL data.
01098                 for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
01099                         iter != mLoadedCallbackList.end(); )
01100                 {
01101                         callback_list_t::iterator curiter = iter++;
01102                         LLLoadedCallbackEntry *entryp = *curiter;
01103                         if (!entryp->mNeedsImageRaw && (entryp->mLastUsedDiscard > gl_discard))
01104                         {
01105                                 BOOL final = gl_discard <= entryp->mDesiredDiscard ? TRUE : FALSE;
01106                                 entryp->mLastUsedDiscard = gl_discard;
01107                                 entryp->mCallback(TRUE, this, NULL, NULL, gl_discard, final, entryp->mUserData);
01108                                 if (final)
01109                                 {
01110                                         iter = mLoadedCallbackList.erase(curiter);
01111                                         delete entryp;
01112                                 }
01113                                 res = true;
01114                         }
01115                 }
01116         }
01117 
01118         //
01119         // If we have no callbacks, take us off of the image callback list.
01120         //
01121         if (mLoadedCallbackList.empty())
01122         {
01123                 gImageList.mCallbackList.erase(this);
01124         }
01125 
01126         // Done with any raw image data at this point (will be re-created if we still have callbacks)
01127         destroyRawImage();
01128         
01129         return res;
01130 }
01131 
01132 //============================================================================
01133 
01134 // Call with 0,0 to turn this feature off.
01135 void LLViewerImage::setKnownDrawSize(S32 width, S32 height)
01136 {
01137         mKnownDrawWidth = width;
01138         mKnownDrawHeight = height;
01139         addTextureStats((F32)(width * height));
01140 }
01141 
01142 // virtual
01143 BOOL LLViewerImage::bind(S32 stage) const
01144 {
01145         if (stage == -1)
01146         {
01147                 return TRUE;
01148         }
01149         
01150         if (gNoRender)
01151         {
01152                 return true;
01153         }
01154         BOOL res = bindTextureInternal(stage);
01155         if (res)
01156         {
01157                 //llassert_always(mIsMissingAsset == FALSE);
01158                 
01159         }
01160         else
01161         {
01162                 // On failure to bind, what should we set the currently bound texture to?
01163                 if (mIsMissingAsset && !sMissingAssetImagep.isNull() && (this != (LLImageGL *)sMissingAssetImagep))
01164                 {
01165                         res = sMissingAssetImagep->bind( stage );
01166                 }
01167                 if (!res && !sDefaultImagep.isNull() && (this != (LLImageGL *)sDefaultImagep))
01168                 {
01169                         // use default if we've got it
01170                         res = sDefaultImagep->bind(stage);
01171                 }
01172                 if (!res && !sNullImagep.isNull() && (this != (LLImageGL *)sNullImagep))
01173                 {
01174                         res = sNullImagep->bind(stage);
01175                 }
01176                 if (!res)
01177                 {
01178                         llwarns << "LLViewerImage::bindTexture failed." << llendl;
01179                 }
01180                 stop_glerror();
01181         }
01182         return res;
01183 }
01184 
01185 // Was in LLImageGL
01186 LLImageRaw* LLViewerImage::createRawImage(S8 discard_level, BOOL allocate)
01187 {
01188         llassert(discard_level >= 0);
01189         if (mRawImage.notNull())
01190         {
01191                 llerrs << "createRawImage() called with existing mRawImage" << llendl;
01192                 mRawImage = NULL;
01193                 mAuxRawImage = NULL;
01194         }
01195         if (allocate && mComponents)
01196         {
01197                 mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), mComponents);
01198                 mIsRawImageValid = TRUE;
01199         }
01200         else
01201         {
01202                 mRawImage = new LLImageRaw;
01203                 mIsRawImageValid = FALSE;
01204         }
01205         mRawDiscardLevel = discard_level;
01206         
01207         return mRawImage;
01208 }
01209 
01210 void LLViewerImage::destroyRawImage()
01211 {
01212         mRawImage = NULL;
01213         mAuxRawImage = NULL;
01214         mIsRawImageValid = FALSE;
01215         mRawDiscardLevel = INVALID_DISCARD_LEVEL;
01216 }

Generated on Thu Jul 1 06:09:27 2010 for Second Life Viewer by  doxygen 1.4.7