llviewerimagelist.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llviewerimagelist.h"
00035 
00036 #include "imageids.h"
00037 #include "llgl.h" // fot gathering stats from GL
00038 #include "llimagegl.h"
00039 #include "llimagebmp.h"
00040 #include "llimagej2c.h"
00041 #include "llimagetga.h"
00042 #include "llimagejpeg.h"
00043 #include "llimagepng.h"
00044 
00045 #include "llsdserialize.h"
00046 #include "llsys.h"
00047 #include "llvfs.h"
00048 #include "llvfile.h"
00049 #include "llvfsthread.h"
00050 #include "message.h"
00051 
00052 #include "llagent.h"
00053 #include "lltexturecache.h"
00054 #include "lltexturefetch.h"
00055 #include "llviewercontrol.h"
00056 #include "llviewerimage.h"
00057 #include "llviewermedia.h"
00058 #include "llviewerregion.h"
00059 #include "pipeline.h"
00060 #include "llappviewer.h"
00061 
00062 #include <sys/stat.h>
00063 
00065 
00066 void (*LLViewerImageList::sUUIDCallback)(void **, const LLUUID&) = NULL;
00067 
00068 U32 LLViewerImageList::sTextureBits = 0;
00069 U32 LLViewerImageList::sTexturePackets = 0;
00070 
00071 const S32 IMAGES_PER_REQUEST = 42;
00072 const S32 IMAGES_MIN_UPDATES = 4;  // Always update the highest N images each frame
00073 const S32 IMAGES_MAX_PACKET_UPDATES = 1; // Only send N packets of IMAGES_PER_REQUEST in a frame
00074 const F32 RESEND_IMAGE_REQUEST_TIME = 15.f; // seconds
00075 
00076 LLViewerImageList gImageList;
00077 
00078 S32 LLViewerImageList::sNumImages = 0;
00079 LLStat LLViewerImageList::sNumImagesStat(32, TRUE);
00080 LLStat LLViewerImageList::sNumRawImagesStat(32, TRUE);
00081 LLStat LLViewerImageList::sGLTexMemStat(32, TRUE);
00082 LLStat LLViewerImageList::sGLBoundMemStat(32, TRUE);
00083 LLStat LLViewerImageList::sRawMemStat(32, TRUE);
00084 LLStat LLViewerImageList::sFormattedMemStat(32, TRUE);
00085 
00087 
00088 LLViewerImageList::LLViewerImageList() 
00089         : mForceResetTextureStats(FALSE),
00090         mUpdateStats(FALSE),
00091         mMaxResidentTexMem(0)
00092 {
00093 }
00094 
00095 void LLViewerImageList::init()
00096 {
00097         sNumImages = 0;
00098         mMaxResidentTexMem = 0;
00099         
00100         if (gNoRender)
00101         {
00102                 // Don't initialize GL stuff if we're not rendering.
00103                 return;
00104         }
00105         
00106         mUpdateStats = TRUE;
00107         
00108         // Update how much texture RAM we're allowed to use.
00109         updateMaxResidentTexMem(0); // 0 = use current
00110         
00111         doPreloadImages();
00112 }
00113 
00114 
00115 void LLViewerImageList::doPreloadImages()
00116 {
00117         LL_DEBUGS("ViewerImages") << "Preloading images..." << LL_ENDL;
00118         
00119         // Set the "missing asset" image
00120         LLViewerImage::sMissingAssetImagep = getImageFromFile("missing_asset.tga");
00121         
00122         // Set the "white" image
00123         LLViewerImage::sWhiteImagep = getImageFromFile("white.tga");
00124         
00125         LLUIImageList* image_list = LLUIImageList::getInstance();
00126 
00127         image_list->initFromFile(gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "textures", "textures.xml"));
00128 
00129         // prefetch specific UUIDs
00130         getImage(IMG_SHOT, TRUE);
00131         getImage(IMG_SMOKE_POOF, TRUE);
00132         LLViewerImage* image = getImageFromFile("silhouette.j2c", MIPMAP_YES, IMMEDIATE_YES);
00133         if (image) 
00134         {
00135                 image->setClamp(FALSE, FALSE);  
00136                 mImagePreloads.insert(image);
00137         }
00138         image = getImageFromFile("noentrylines.j2c", MIPMAP_YES, IMMEDIATE_YES);
00139         if (image) 
00140         {
00141                 image->setClamp(FALSE, FALSE);  
00142                 mImagePreloads.insert(image);
00143         }
00144         image = getImageFromFile("noentrypasslines.j2c", MIPMAP_YES, IMMEDIATE_YES);
00145         if (image) 
00146         {
00147                 image->setClamp(FALSE, FALSE);  
00148                 mImagePreloads.insert(image);
00149         }
00150         image = getImage(DEFAULT_WATER_NORMAL, MIPMAP_YES, IMMEDIATE_YES);
00151         if (image) 
00152         {
00153                 image->setClamp(FALSE, FALSE);  
00154                 mImagePreloads.insert(image);
00155         }
00156 }
00157 
00158 static std::string get_texture_list_name()
00159 {
00160         BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation");
00161         return std::string("texture_list_") + (login_last?"last":"home") + ".xml";
00162 }
00163 
00164 void LLViewerImageList::doPrefetchImages()
00165 {
00166     if (LLAppViewer::instance()->getPurgeCache())
00167         {
00168                 // cache was purged, no point
00169                 return;
00170         }
00171         
00172         // Pre-fetch textures from last logout
00173         LLSD imagelist;
00174         std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name());
00175         llifstream file;
00176         file.open(filename.c_str());
00177         if (file.is_open())
00178         {
00179                 LLSDSerialize::fromXML(imagelist, file);
00180         }
00181         for (LLSD::array_iterator iter = imagelist.beginArray();
00182                  iter != imagelist.endArray(); ++iter)
00183         {
00184                 LLSD imagesd = *iter;
00185                 LLUUID uuid = imagesd["uuid"];
00186                 S32 pixel_area = imagesd["area"];
00187                 LLViewerImage* image = getImage(uuid, MIPMAP_TRUE, FALSE);
00188                 if (image)
00189                 {
00190                         image->addTextureStats((F32)pixel_area);
00191                 }
00192         }
00193         
00194         
00195 }
00196 
00198 
00199 LLViewerImageList::~LLViewerImageList()
00200 {
00201         llassert(mIRCallbackData.empty());
00202 }
00203 
00204 void LLViewerImageList::shutdown()
00205 {
00206         // clear out preloads
00207         mImagePreloads.clear();
00208 
00209         // Write out list of currently loaded textures for precaching on startup
00210         typedef std::set<std::pair<S32,LLViewerImage*> > image_area_list_t;
00211         image_area_list_t image_area_list;
00212         for (image_priority_list_t::iterator iter = mImageList.begin();
00213                  iter != mImageList.end(); ++iter)
00214         {
00215                 LLViewerImage* image = *iter;
00216                 if (!image->getUseDiscard() ||
00217                         image->needsAux() ||
00218                         image->getTargetHost() != LLHost::invalid)
00219                 {
00220                         continue; // avoid UI, baked, and other special images
00221                 }
00222                 S32 desired = image->getDesiredDiscardLevel();
00223                 if (desired >= 0 && desired < MAX_DISCARD_LEVEL)
00224                 {
00225                         S32 pixel_area = image->getWidth(desired) * image->getHeight(desired);
00226                         image_area_list.insert(std::make_pair(pixel_area, image));
00227                 }
00228         }
00229         
00230         LLSD imagelist;
00231         const S32 max_count = 1000;
00232         S32 count = 0;
00233         for (image_area_list_t::reverse_iterator riter = image_area_list.rbegin();
00234                  riter != image_area_list.rend(); ++riter)
00235         {
00236                 LLViewerImage* image = riter->second;
00237                 imagelist[count]["area"] = riter->first;
00238                 imagelist[count]["uuid"] = image->getID();
00239                 if (++count >= max_count)
00240                         break;
00241         }
00242         
00243         if (count > 0 && !gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "").empty())
00244         {
00245                 std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name());
00246                 llofstream file;
00247                 file.open(filename.c_str());
00248                 LLSDSerialize::toPrettyXML(imagelist, file);
00249         }
00250         
00251         //
00252         // Clean up "loaded" callbacks.
00253         //
00254         mCallbackList.clear();
00255         mIRCallbackData.clear();
00256         
00257         // Flush all of the references
00258         mLoadingStreamList.clear();
00259         mCreateTextureList.clear();
00260         
00261         mUUIDMap.clear();
00262         
00263         mImageList.clear();
00264 }
00265 
00266 void LLViewerImageList::dump()
00267 {
00268         llinfos << "LLViewerImageList::dump()" << llendl;
00269         for (image_priority_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it)
00270         {
00271                 LLViewerImage* image = *it;
00272                 
00273                 llinfos << "priority " << image->getDecodePriority()
00274                 << " boost " << image->getBoostLevel()
00275                 << " size " << image->getWidth() << "x" << image->getHeight()
00276                 << " discard " << image->getDiscardLevel()
00277                 << " desired " << image->getDesiredDiscardLevel()
00278                 << " http://asset.siva.lindenlab.com/" << image->getID() << ".texture"
00279                 << llendl;
00280         }
00281 }
00282 
00283 void LLViewerImageList::destroyGL(BOOL save_state)
00284 {
00285         LLImageGL::destroyGL(save_state);
00286 }
00287 
00288 void LLViewerImageList::restoreGL()
00289 {
00290         LLImageGL::restoreGL();
00291 }
00292 
00293 /* Vertical tab container button image IDs
00294  Seem to not decode when running app in debug.
00295  
00296  const LLUUID BAD_IMG_ONE("1097dcb3-aef9-8152-f471-431d840ea89e");
00297  const LLUUID BAD_IMG_TWO("bea77041-5835-1661-f298-47e2d32b7a70");
00298  */
00299 
00301 
00302 LLViewerImage* LLViewerImageList::getImageFromFile(const LLString& filename,
00303                                                                                                    BOOL usemipmaps,
00304                                                                                                    BOOL level_immediate,
00305                                                                                                    LLGLint internal_format,
00306                                                                                                    LLGLenum primary_format, 
00307                                                                                                    const LLUUID& force_id)
00308 {
00309         if (gNoRender)
00310         {
00311                 // Never mind that this ignores image_set_id;
00312                 // getImage() will handle that later.
00313                 return getImage(IMG_DEFAULT, TRUE, TRUE);
00314         }
00315 
00316         if (filename.empty())
00317         {
00318                 return getImage(IMG_DEFAULT, TRUE, TRUE);
00319         }
00320 
00321         // generate UUID based on hash of filename
00322         LLUUID new_id;
00323         if (force_id.notNull())
00324         {
00325                 new_id = force_id;
00326         }
00327         else
00328         {
00329                 new_id.generate(std::string(filename));
00330         }
00331 
00332         LLPointer<LLViewerImage> imagep = hasImage(new_id);
00333         
00334         if (imagep.isNull())
00335         {
00336                 imagep = new LLViewerImage(filename, new_id, usemipmaps);
00337                 
00338                 if (internal_format && primary_format)
00339                 {
00340                         imagep->setExplicitFormat(internal_format, primary_format);
00341                 }
00342 
00343                 addImage(imagep);
00344                 
00345                 if (level_immediate)
00346                 {
00347                         imagep->dontDiscard();
00348                         imagep->setBoostLevel(LLViewerImage::BOOST_UI);
00349                 }
00350         }
00351 
00352         return imagep;
00353 }
00354 
00355 
00356 LLViewerImage* LLViewerImageList::getImage(const LLUUID &image_id,
00357                                                                                                    BOOL usemipmaps,
00358                                                                                                    BOOL level_immediate,
00359                                                                                                    LLGLint internal_format,
00360                                                                                                    LLGLenum primary_format,
00361                                                                                                    LLHost request_from_host)
00362 {
00363         // Return the image with ID image_id
00364         // If the image is not found, creates new image and
00365         // enqueues a request for transmission
00366         
00367         if ((&image_id == NULL) || image_id.isNull())
00368         {
00369                 return (getImage(IMG_DEFAULT, TRUE, TRUE));
00370         }
00371         
00372         LLPointer<LLViewerImage> imagep = hasImage(image_id);
00373         
00374         if (imagep.isNull())
00375         {
00376                 imagep = new LLViewerImage(image_id, usemipmaps);
00377                 // Might want to request from host other than where the agent is. JC
00378                 imagep->setTargetHost(request_from_host);
00379                 
00380                 if (internal_format && primary_format)
00381                 {
00382                         imagep->setExplicitFormat(internal_format, primary_format);
00383                 }
00384                 
00385                 addImage(imagep);
00386                 
00387                 if (level_immediate)
00388                 {
00389                         imagep->dontDiscard();
00390                         imagep->setBoostLevel(LLViewerImage::BOOST_UI);
00391                 }
00392         }
00393         
00394         return imagep;
00395 }
00396 
00397 LLViewerImage *LLViewerImageList::hasImage(const LLUUID &image_id)
00398 {
00399         uuid_map_t::iterator iter = mUUIDMap.find(image_id);
00400         if(iter == mUUIDMap.end())
00401                 return NULL;
00402         return iter->second;
00403 }
00404 
00405 void LLViewerImageList::addImageToList(LLViewerImage *image)
00406 {
00407         llassert(image);
00408         if (image->mInImageList)
00409         {
00410                 llerrs << "LLViewerImageList::addImageToList - Image already in list" << llendl;
00411         }
00412         llverify((mImageList.insert(image)).second == true);
00413         image->mInImageList = TRUE;
00414 }
00415 
00416 void LLViewerImageList::removeImageFromList(LLViewerImage *image)
00417 {
00418         llassert(image);
00419         if (!image->mInImageList)
00420         {
00421                 llerrs << "LLViewerImageList::removeImageFromList - Image not in list" << llendl;
00422         }
00423         llverify(mImageList.erase(image) == 1);
00424         image->mInImageList = FALSE;
00425 }
00426 
00427 void LLViewerImageList::addImage(LLViewerImage *new_image)
00428 {
00429         if (!new_image)
00430         {
00431                 llwarning("No image to add to image list", 0);
00432                 return;
00433         }
00434         LLUUID image_id = new_image->getID();
00435         
00436         LLViewerImage *image = hasImage(image_id);
00437         if (image)
00438         {
00439                 llerrs << "Image with ID " << image_id << " already in list" << llendl;
00440         }
00441         sNumImages++;
00442         
00443         addImageToList(new_image);
00444         mUUIDMap[image_id] = new_image;
00445 }
00446 
00447 
00448 void LLViewerImageList::deleteImage(LLViewerImage *image)
00449 {
00450         if( image)
00451         {
00452                 if (image->hasCallbacks())
00453                 {
00454                         mCallbackList.erase((LLViewerImage*)image);
00455                 }
00456 
00457                 llverify(mUUIDMap.erase(image->getID()) == 1);
00458                 sNumImages--;
00459                 removeImageFromList(image);
00460         }
00461 }
00462 
00464 
00465 
00467 
00468 void LLViewerImageList::dirtyImage(LLViewerImage *image)
00469 {
00470         mDirtyTextureList.insert(image);
00471 }
00472 
00474 
00475 void LLViewerImageList::updateImages(F32 max_time)
00476 {
00477         sNumImagesStat.addValue(sNumImages);
00478         sNumRawImagesStat.addValue(LLImageRaw::sRawImageCount);
00479         sGLTexMemStat.addValue(LLImageGL::sGlobalTextureMemory/(1024.f*1024.f));
00480         sGLBoundMemStat.addValue(LLImageGL::sBoundTextureMemory/(1024.f*1024.f));
00481         sRawMemStat.addValue(LLImageRaw::sGlobalRawMemory/(1024.f*1024.f));
00482         sFormattedMemStat.addValue(LLImageFormatted::sGlobalFormattedMemory/(1024.f*1024.f));
00483         
00484         updateImagesDecodePriorities();
00485         max_time -= updateImagesFetchTextures(max_time);
00486         max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f);
00487         max_time -= updateImagesCreateTextures(max_time);
00488         max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f);
00489         
00490         if (!mDirtyTextureList.empty())
00491         {
00492                 LLFastTimer t(LLFastTimer::FTM_IMAGE_MARK_DIRTY);
00493                 gPipeline.dirtyPoolObjectTextures(mDirtyTextureList);
00494                 mDirtyTextureList.clear();
00495         }
00496 
00497         bool didone = false;
00498         for (image_list_t::iterator iter = mCallbackList.begin();
00499                 iter != mCallbackList.end(); )
00500         {
00501                 //trigger loaded callbacks on local textures immediately
00502                 LLViewerImage* image = *iter++;
00503                 if (!image->mLocalFileName.empty())
00504                 {
00505                         // Do stuff to handle callbacks, update priorities, etc.
00506                         didone = image->doLoadedCallbacks();
00507                 }
00508                 else if (!didone)
00509                 {
00510                         // Do stuff to handle callbacks, update priorities, etc.
00511                         didone = image->doLoadedCallbacks();
00512                 }
00513         }
00514 
00515         if (!gNoRender && !gGLManager.mIsDisabled)
00516         {
00517                 LLViewerMedia::updateImagesMediaStreams();
00518         }
00519 
00520         updateImagesUpdateStats();
00521 }
00522 
00523 void LLViewerImageList::updateImagesDecodePriorities()
00524 {
00525         // Update the decode priority for N images each frame
00526         {
00527                 const size_t max_update_count = llmin((S32) (1024*gFrameIntervalSeconds) + 1, 32); //target 1024 textures per second
00528                 S32 update_counter = llmin(max_update_count, mUUIDMap.size()/10);
00529                 uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID);
00530                 while(update_counter > 0)
00531                 {
00532                         if (iter == mUUIDMap.end())
00533                         {
00534                                 iter = mUUIDMap.begin();
00535                         }
00536                         mLastUpdateUUID = iter->first;
00537                         LLPointer<LLViewerImage> imagep = iter->second;
00538                         ++iter; // safe to incrament now
00539 
00540                         //
00541                         // Flush formatted images using a lazy flush
00542                         //
00543                         const F32 LAZY_FLUSH_TIMEOUT = 30.f;
00544                         S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference
00545                         if (imagep->hasCallbacks())
00546                         {
00547                                 min_refs++; // Add an extra reference if we're on the loaded callback list
00548                         }
00549                         S32 num_refs = imagep->getNumRefs();
00550                         if (num_refs == min_refs)
00551                         {
00552                                 if (imagep->mLastReferencedTimer.getElapsedTimeF32() > LAZY_FLUSH_TIMEOUT)
00553                                 {
00554                                         // Remove the unused image from the image list
00555                                         deleteImage(imagep);
00556                                         imagep = NULL; // should destroy the image
00557                                         continue;
00558                                 }
00559                         }
00560                         else
00561                         {
00562                                 imagep->mLastReferencedTimer.reset();
00563                         }
00564                         
00565                         imagep->processTextureStats();
00566                         F32 old_priority = imagep->getDecodePriority();
00567                         F32 decode_priority = imagep->calcDecodePriority();
00568                         // Ignore < 20% difference
00569                         if ((decode_priority < old_priority * .8f || decode_priority > old_priority * 1.25f))
00570                         {
00571                                 removeImageFromList(imagep);
00572                                 imagep->setDecodePriority(decode_priority);
00573                                 addImageToList(imagep);
00574                         }
00575                         update_counter--;
00576                 }
00577         }
00578 }
00579 
00580 /*
00581  static U8 get_image_type(LLViewerImage* imagep, LLHost target_host)
00582  {
00583  // Having a target host implies this is a baked image.  I don't
00584  // believe that boost level has been set at this point. JC
00585  U8 type_from_host = (target_host.isOk() 
00586  ? LLImageBase::TYPE_AVATAR_BAKE 
00587  : LLImageBase::TYPE_NORMAL);
00588  S32 boost_level = imagep->getBoostLevel();
00589  U8 type_from_boost = ( (boost_level == LLViewerImage::BOOST_AVATAR_BAKED 
00590  || boost_level == LLViewerImage::BOOST_AVATAR_BAKED_SELF)
00591  ? LLImageBase::TYPE_AVATAR_BAKE 
00592  : LLImageBase::TYPE_NORMAL);
00593  if (type_from_host == LLImageBase::TYPE_NORMAL
00594  && type_from_boost == LLImageBase::TYPE_AVATAR_BAKE)
00595  {
00596  llwarns << "TAT: get_image_type() type_from_host doesn't match type_from_boost"
00597  << " host " << target_host
00598  << " boost " << imagep->getBoostLevel()
00599  << " imageid " << imagep->getID()
00600  << llendl;
00601  imagep->dump();
00602  }
00603  return type_from_host;
00604  }
00605  */
00606 
00607 F32 LLViewerImageList::updateImagesCreateTextures(F32 max_time)
00608 {
00609         if (gNoRender || gGLManager.mIsDisabled) return 0.0f;
00610         
00611         //
00612         // Create GL textures for all textures that need them (images which have been
00613         // decoded, but haven't been pushed into GL).
00614         //
00615         LLFastTimer t(LLFastTimer::FTM_IMAGE_CREATE);
00616         
00617         LLTimer create_timer;
00618         image_list_t::iterator enditer = mCreateTextureList.begin();
00619         for (image_list_t::iterator iter = mCreateTextureList.begin();
00620                  iter != mCreateTextureList.end();)
00621         {
00622                 image_list_t::iterator curiter = iter++;
00623                 enditer = iter;
00624                 LLViewerImage *imagep = *curiter;
00625                 imagep->createTexture();
00626                 if (create_timer.getElapsedTimeF32() > max_time)
00627                 {
00628                         break;
00629                 }
00630         }
00631         mCreateTextureList.erase(mCreateTextureList.begin(), enditer);
00632         return create_timer.getElapsedTimeF32();
00633 }
00634 
00635 F32 LLViewerImageList::updateImagesFetchTextures(F32 max_time)
00636 {
00637         LLTimer image_op_timer;
00638         
00639         // Update the decode priority for N images each frame
00640         // Make a list with 32 high priority entries + 256 cycled entries
00641         const size_t max_priority_count = llmin((S32) (256*10.f*gFrameIntervalSeconds)+1, 32);
00642         const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256);
00643         
00644         // 32 high priority entries
00645         std::set<LLViewerImage*> entries;
00646         size_t update_counter = llmin(max_priority_count, mImageList.size());
00647         image_priority_list_t::iterator iter1 = mImageList.begin();
00648         while(update_counter > 0)
00649         {
00650                 // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad
00651                 if(iter1 == mImageList.end())
00652                 {
00653                         llerrs << "DEV-12002: update_counter not calculated correctly!" << llendl;
00654                 }
00655 
00656                 LLPointer<LLViewerImage> const & ptr = *iter1;
00657 
00658                 LLViewerImage * img = ptr.get();
00659 
00660                 // added extra granularity and verbosity for crash logging during 1.19.1 RC. -Brad
00661                 if(img == NULL)
00662                 {
00663                         llwarns << "DEV-12002: image is NULL!" << llendl;
00664                 }
00665 
00666                 entries.insert(img);
00667 
00668                 ++iter1;
00669                 update_counter--;
00670         }
00671         
00672         // 256 cycled entries
00673         update_counter = llmin(max_update_count, mUUIDMap.size());
00674         uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID);
00675         while(update_counter > 0)
00676         {
00677                 if (iter2 == mUUIDMap.end())
00678                 {
00679                         iter2 = mUUIDMap.begin();
00680                 }
00681                 mLastFetchUUID = iter2->first;
00682                 entries.insert(iter2->second);
00683                 ++iter2;
00684                 update_counter--;
00685         }
00686         
00687         S32 min_count = max_priority_count + max_update_count/4;
00688         for (std::set<LLViewerImage*>::iterator iter3 = entries.begin();
00689                  iter3 != entries.end(); )
00690         {
00691                 LLPointer<LLViewerImage> imagep = *iter3++;
00692                 
00693                 imagep->updateFetch();
00694                 if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time)
00695                 {
00696                         break;
00697                 }
00698                 min_count--;
00699         }
00700         return image_op_timer.getElapsedTimeF32();
00701 }
00702 
00703 void LLViewerImageList::updateImagesUpdateStats()
00704 {
00705         if (mUpdateStats)
00706         {
00707                 for (image_priority_list_t::iterator iter = mImageList.begin();
00708                          iter != mImageList.end(); )
00709                 {
00710                         LLViewerImage* imagep = *iter++;
00711                         imagep->resetTextureStats(mForceResetTextureStats);
00712                 }
00713                 mUpdateStats = FALSE;
00714                 mForceResetTextureStats = FALSE;
00715         }
00716 }
00717 
00718 void LLViewerImageList::decodeAllImages(F32 max_time)
00719 {
00720         LLTimer timer;
00721         if(gNoRender) return;
00722         
00723         // Update texture stats and priorities
00724         std::vector<LLPointer<LLViewerImage> > image_list;
00725         for (image_priority_list_t::iterator iter = mImageList.begin();
00726                  iter != mImageList.end(); )
00727         {
00728                 LLViewerImage* imagep = *iter++;
00729                 image_list.push_back(imagep);
00730                 imagep->mInImageList = FALSE;
00731         }
00732         mImageList.clear();
00733         for (std::vector<LLPointer<LLViewerImage> >::iterator iter = image_list.begin();
00734                  iter != image_list.end(); ++iter)
00735         {
00736                 LLViewerImage* imagep = *iter;
00737                 imagep->processTextureStats();
00738                 F32 decode_priority = imagep->calcDecodePriority();
00739                 imagep->setDecodePriority(decode_priority);
00740                 mImageList.insert(imagep);
00741                 imagep->mInImageList = TRUE;
00742         }
00743         image_list.clear();
00744         
00745         // Update fetch (decode)
00746         for (image_priority_list_t::iterator iter = mImageList.begin();
00747                  iter != mImageList.end(); )
00748         {
00749                 LLViewerImage* imagep = *iter++;
00750                 imagep->updateFetch();
00751         }
00752         // Run threads
00753         S32 fetch_pending = 0;
00754         while (1)
00755         {
00756                 LLAppViewer::instance()->getTextureCache()->update(1); // unpauses the texture cache thread
00757                 LLAppViewer::instance()->getImageDecodeThread()->update(1); // unpauses the image thread
00758                 fetch_pending = LLAppViewer::instance()->getTextureFetch()->update(1); // unpauses the texture fetch thread
00759                 if (fetch_pending == 0 || timer.getElapsedTimeF32() > max_time)
00760                 {
00761                         break;
00762                 }
00763         }
00764         // Update fetch again
00765         for (image_priority_list_t::iterator iter = mImageList.begin();
00766                  iter != mImageList.end(); )
00767         {
00768                 LLViewerImage* imagep = *iter++;
00769                 imagep->updateFetch();
00770         }
00771         max_time -= timer.getElapsedTimeF32();
00772         max_time = llmax(max_time, .001f);
00773         F32 create_time = updateImagesCreateTextures(max_time);
00774         
00775         LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " 
00776         << " fetch_pending " << fetch_pending
00777         << " create_time " << create_time
00778         << LL_ENDL;
00779 }
00780 
00781 
00782 BOOL LLViewerImageList::createUploadFile(const LLString& filename,
00783                                                                                  const LLString& out_filename,
00784                                                                                  const U8 codec)
00785 {
00786         // First, load the image.
00787         LLPointer<LLImageRaw> raw_image = new LLImageRaw;
00788         
00789         switch (codec)
00790         {
00791                 case IMG_CODEC_BMP:
00792                 {
00793                         LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
00794                         
00795                         if (!bmp_image->load(filename))
00796                         {
00797                                 return FALSE;
00798                         }
00799                         
00800                         if (!bmp_image->decode(raw_image, 0.0f))
00801                         {
00802                                 return FALSE;
00803                         }
00804                 }
00805                         break;
00806                 case IMG_CODEC_TGA:
00807                 {
00808                         LLPointer<LLImageTGA> tga_image = new LLImageTGA;
00809                         
00810                         if (!tga_image->load(filename))
00811                         {
00812                                 return FALSE;
00813                         }
00814                         
00815                         if (!tga_image->decode(raw_image))
00816                         {
00817                                 return FALSE;
00818                         }
00819                         
00820                         if(     (tga_image->getComponents() != 3) &&
00821                            (tga_image->getComponents() != 4) )
00822                         {
00823                                 tga_image->setLastError( "Image files with less than 3 or more than 4 components are not supported." );
00824                                 return FALSE;
00825                         }
00826                 }
00827                         break;
00828                 case IMG_CODEC_JPEG:
00829                 {
00830                         LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
00831                         
00832                         if (!jpeg_image->load(filename))
00833                         {
00834                                 return FALSE;
00835                         }
00836                         
00837                         if (!jpeg_image->decode(raw_image, 0.0f))
00838                         {
00839                                 return FALSE;
00840                         }
00841                 }
00842                         break;
00843                 case IMG_CODEC_PNG:
00844                 {
00845                         LLPointer<LLImagePNG> png_image = new LLImagePNG;
00846                         
00847                         if (!png_image->load(filename))
00848                         {
00849                                 return FALSE;
00850                         }
00851                         
00852                         if (!png_image->decode(raw_image, 0.0f))
00853                         {
00854                                 return FALSE;
00855                         }
00856                 }
00857                         break;
00858                 default:
00859                         return FALSE;
00860         }
00861         
00862         LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image);
00863         
00864         if( !compressedImage->save(out_filename) )
00865         {
00866                 llinfos << "Couldn't create output file " << out_filename << llendl;
00867                 return FALSE;
00868         }
00869         
00870         // test to see if the encode and save worked.
00871         LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
00872         if( !integrity_test->loadAndValidate( out_filename ) )
00873         {
00874                 llinfos << "Image: " << out_filename << " is corrupt." << llendl;
00875                 return FALSE;
00876         }
00877         
00878         return TRUE;
00879 }
00880 
00881 // note: modifies the argument raw_image!!!!
00882 LLPointer<LLImageJ2C> LLViewerImageList::convertToUploadFile(LLPointer<LLImageRaw> raw_image)
00883 {
00884         raw_image->biasedScaleToPowerOfTwo(LLViewerImage::MAX_IMAGE_SIZE_DEFAULT);
00885         LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C();
00886         compressedImage->setRate(0.f);
00887         
00888         if (gSavedSettings.getBOOL("LosslessJ2CUpload") &&
00889                 (raw_image->getWidth() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF) &&
00890                 (raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF))
00891                 compressedImage->setReversible(TRUE);
00892         
00893         compressedImage->encode(raw_image, 0.0f);
00894         
00895         return compressedImage;
00896 }
00897 
00898 const S32 MIN_VIDEO_RAM = 32;
00899 const S32 MAX_VIDEO_RAM = 2048;
00900         
00901 // Returns min setting for TextureMemory (in MB)
00902 S32 LLViewerImageList::getMinVideoRamSetting()
00903 {
00904         return MIN_VIDEO_RAM;
00905 }
00906 
00907 //static
00908 // Returns max setting for TextureMemory (in MB)
00909 S32 LLViewerImageList::getMaxVideoRamSetting(bool get_recommended)
00910 {
00911         S32 max_texmem;
00912         if (gGLManager.mVRAM != 0)
00913         {
00914                 // Treat any card with < 32 MB (shudder) as having 32 MB
00915                 //  - it's going to be swapping constantly regardless
00916                 S32 max_vram = gGLManager.mVRAM;
00917                 max_vram = llmax(max_vram, getMinVideoRamSetting());
00918                 max_texmem = max_vram;
00919                 if (!get_recommended)
00920                         max_texmem *= 2;
00921         }
00922         else
00923         {
00924                 if (get_recommended)
00925                         max_texmem = 128;
00926                 else
00927                         max_texmem = 512;
00928                 llwarns << "VRAM amount not detected, defaulting to " << max_texmem << " MB" << llendl;
00929         }
00930 
00931         S32 system_ram = (S32)(gSysMemory.getPhysicalMemoryClamped() >> 20); // In MB
00932         //llinfos << "*** DETECTED " << system_ram << " MB of system memory." << llendl;
00933         if (get_recommended)
00934                 max_texmem = llmin(max_texmem, (S32)(system_ram/2));
00935         else
00936                 max_texmem = llmin(max_texmem, (S32)(system_ram));
00937         
00938         max_texmem = llclamp(max_texmem, MIN_VIDEO_RAM, MAX_VIDEO_RAM);
00939         
00940         return max_texmem;
00941 }
00942 
00943 const S32 VIDEO_CARD_FRAMEBUFFER_MEM = 12; // MB
00944 
00945 void LLViewerImageList::updateMaxResidentTexMem(S32 mem)
00946 {
00947         // Initialize the image pipeline VRAM settings
00948         S32 cur_mem = gSavedSettings.getS32("TextureMemory");
00949         F32 mem_multiplier = gSavedSettings.getF32("RenderTextureMemoryMultiple");
00950         S32 default_mem = getMaxVideoRamSetting(true); // recommended default
00951         if (mem == 0)
00952         {
00953                 mem = cur_mem > 0 ? cur_mem : default_mem;
00954         }
00955         else if (mem < 0)
00956         {
00957                 mem = default_mem;
00958         }
00959 
00960         // limit the texture memory to a multiple of the default if we've found some cards to behave poorly otherwise
00961         mem = llmin(mem, (S32) (mem_multiplier * (F32) default_mem));
00962 
00963         mem = llclamp(mem, getMinVideoRamSetting(), getMaxVideoRamSetting());
00964         if (mem != cur_mem)
00965         {
00966                 gSavedSettings.setS32("TextureMemory", mem);
00967                 return; //listener will re-enter this function
00968         }
00969 
00970         // TODO: set available resident texture mem based on use by other subsystems
00971         // currently max(12MB, VRAM/4) assumed...
00972         
00973         S32 vb_mem = mem;
00974         S32 fb_mem = llmax(VIDEO_CARD_FRAMEBUFFER_MEM, vb_mem/4);
00975         mMaxResidentTexMem = (vb_mem - fb_mem)<<20;
00976         
00977         llinfos << "Total Video Memory set to: " << vb_mem << " MB" << llendl;
00978         llinfos << "Available Texture Memory set to: " << (vb_mem - fb_mem) << " MB" << llendl;
00979 }
00980 
00982 
00983 // static
00984 void LLViewerImageList::receiveImageHeader(LLMessageSystem *msg, void **user_data)
00985 {
00986         LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
00987         
00988         // Receive image header, copy into image object and decompresses 
00989         // if this is a one-packet image. 
00990         
00991         LLUUID id;
00992         
00993         char ip_string[256];
00994         u32_to_ip_string(msg->getSenderIP(),ip_string);
00995         
00996         if (msg->getReceiveCompressedSize())
00997         {
00998                 gImageList.sTextureBits += msg->getReceiveCompressedSize() * 8;
00999         }
01000         else
01001         {
01002                 gImageList.sTextureBits += msg->getReceiveSize() * 8;
01003         }
01004         gImageList.sTexturePackets++;
01005         
01006         U8 codec;
01007         U16 packets;
01008         U32 totalbytes;
01009         msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
01010         msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, codec);
01011         msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, packets);
01012         msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, totalbytes);
01013         
01014         S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
01015         if (!data_size)
01016         {
01017                 return;
01018         }
01019         if (data_size < 0)
01020         {
01021                 // msg->getSizeFast() is probably trying to tell us there
01022                 // was an error.
01023                 llerrs << "image header chunk size was negative: "
01024                 << data_size << llendl;
01025                 return;
01026         }
01027         
01028         // this buffer gets saved off in the packet list
01029         U8 *data = new U8[data_size];
01030         msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
01031         
01032         LLViewerImage *image = gImageList.getImage(id);
01033         if (!image)
01034         {
01035                 delete [] data;
01036                 return;
01037         }
01038         image->mLastPacketTimer.reset();
01039         bool res = LLAppViewer::getTextureFetch()->receiveImageHeader(msg->getSender(), id, codec, packets, totalbytes, data_size, data);
01040         if (!res)
01041         {
01042                 delete[] data;
01043         }
01044 }
01045 
01046 // static
01047 void LLViewerImageList::receiveImagePacket(LLMessageSystem *msg, void **user_data)
01048 {
01049         LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
01050         LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
01051         
01052         // Receives image packet, copy into image object,
01053         // checks if all packets received, decompresses if so. 
01054         
01055         LLUUID id;
01056         U16 packet_num;
01057         
01058         char ip_string[256];
01059         u32_to_ip_string(msg->getSenderIP(),ip_string);
01060         
01061         if (msg->getReceiveCompressedSize())
01062         {
01063                 gImageList.sTextureBits += msg->getReceiveCompressedSize() * 8;
01064         }
01065         else
01066         {
01067                 gImageList.sTextureBits += msg->getReceiveSize() * 8;
01068         }
01069         gImageList.sTexturePackets++;
01070         
01071         //llprintline("Start decode, image header...");
01072         msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
01073         msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num);
01074         S32 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data); 
01075         
01076         if (!data_size)
01077         {
01078                 return;
01079         }
01080         if (data_size < 0)
01081         {
01082                 // msg->getSizeFast() is probably trying to tell us there
01083                 // was an error.
01084                 llerrs << "image data chunk size was negative: "
01085                 << data_size << llendl;
01086                 return;
01087         }
01088         if (data_size > MTUBYTES)
01089         {
01090                 llerrs << "image data chunk too large: " << data_size << " bytes" << llendl;
01091                 return;
01092         }
01093         U8 *data = new U8[data_size];
01094         msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
01095         
01096         LLViewerImage *image = gImageList.getImage(id);
01097         if (!image)
01098         {
01099                 delete [] data;
01100                 return;
01101         }
01102         image->mLastPacketTimer.reset();
01103         bool res = LLAppViewer::getTextureFetch()->receiveImagePacket(msg->getSender(), id, packet_num, data_size, data);
01104         if (!res)
01105         {
01106                 delete[] data;
01107         }
01108 }
01109 
01110 
01111 // We've been that the asset server does not contain the requested image id.
01112 // static
01113 void LLViewerImageList::processImageNotInDatabase(LLMessageSystem *msg,void **user_data)
01114 {
01115         LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
01116         LLUUID image_id;
01117         msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, image_id);
01118         
01119         LLViewerImage* image = gImageList.hasImage( image_id );
01120         if( image )
01121         {
01122                 image->setIsMissingAsset();
01123         }
01124 }
01125 
01127 
01128 //static
01129 const U32 SIXTEEN_MEG = 0x1000000;
01130 S32 LLViewerImageList::calcMaxTextureRAM()
01131 {
01132         // Decide the maximum amount of RAM we should allow the user to allocate to texture cache
01133         LLMemoryInfo memory_info;
01134         U32 available_memory = memory_info.getPhysicalMemoryClamped();
01135         
01136         clamp_rescale((F32)available_memory,
01137                                   (F32)(SIXTEEN_MEG * 16),
01138                                   (F32)U32_MAX,
01139                                   (F32)(SIXTEEN_MEG * 4),
01140                                   (F32)(U32_MAX >> 1));
01141         return available_memory;
01142 }
01143 
01145 
01146 // explicitly cleanup resources, as this is a singleton class with process
01147 // lifetime so ability to perform std::map operations in destructor is not
01148 // guaranteed.
01149 void LLUIImageList::cleanUp()
01150 {
01151         mUIImages.clear();
01152 }
01153 
01154 LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id)
01155 {
01156         // use id as image name
01157         LLString image_name = image_id.asString();
01158 
01159         // look for existing image
01160         uuid_ui_image_map_t::iterator found_it = mUIImages.find(image_name);
01161         if (found_it != mUIImages.end())
01162         {
01163                 return found_it->second;
01164         }
01165 
01166         return loadUIImageByID(image_id);
01167 }
01168 
01169 LLUIImagePtr LLUIImageList::getUIImage(const LLString& image_name)
01170 {
01171         // look for existing image
01172         uuid_ui_image_map_t::iterator found_it = mUIImages.find(image_name);
01173         if (found_it != mUIImages.end())
01174         {
01175                 return found_it->second;
01176         }
01177 
01178         return loadUIImageByName(image_name, image_name);
01179 }
01180 
01181 LLUIImagePtr LLUIImageList::loadUIImageByName(const LLString& name, const LLString& filename, BOOL use_mips, const LLRect& scale_rect)
01182 {
01183         LLViewerImage* imagep = gImageList.getImageFromFile(filename, MIPMAP_NO, IMMEDIATE_YES);
01184         return loadUIImage(imagep, name, use_mips, scale_rect);
01185 }
01186 
01187 LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id, BOOL use_mips, const LLRect& scale_rect)
01188 {
01189         LLViewerImage* imagep = gImageList.getImage(id, MIPMAP_NO, IMMEDIATE_YES);
01190         return loadUIImage(imagep, id.asString(), use_mips, scale_rect);
01191 }
01192 
01193 LLUIImagePtr LLUIImageList::loadUIImage(LLViewerImage* imagep, const LLString& name, BOOL use_mips, const LLRect& scale_rect)
01194 {
01195         if (!imagep) return NULL;
01196 
01197         imagep->setClamp(TRUE, TRUE);
01198 
01199         LLUIImagePtr new_imagep = new LLUIImage(name, imagep);
01200         mUIImages.insert(std::make_pair(name, new_imagep));
01201 
01202         LLUIImageLoadData* datap = new LLUIImageLoadData;
01203         datap->mImageName = name;
01204         datap->mImageScaleRegion = scale_rect;
01205 
01206         imagep->setLoadedCallback(onUIImageLoaded, 0, FALSE, datap);
01207 
01208         return new_imagep;
01209 }
01210 
01211 LLUIImagePtr LLUIImageList::preloadUIImage(const LLString& name, const LLString& filename, BOOL use_mips, const LLRect& scale_rect)
01212 {
01213         // look for existing image
01214         uuid_ui_image_map_t::iterator found_it = mUIImages.find(name);
01215         if (found_it != mUIImages.end())
01216         {
01217                 // image already loaded!
01218                 llerrs << "UI Image " << name << " already loaded." << llendl;
01219         }
01220 
01221         return loadUIImageByName(name, filename, use_mips, scale_rect);
01222 }
01223 
01224 //static 
01225 void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* user_data )
01226 {
01227         if(!success || !user_data) 
01228         {
01229                 return;
01230         }
01231 
01232         LLString ui_image_name ;
01233         LLRect scale_rect ;
01234         {
01235                 LLUIImageLoadData* image_datap = (LLUIImageLoadData*)user_data;
01236 
01237                 ui_image_name = image_datap->mImageName;
01238                 scale_rect = image_datap->mImageScaleRegion;
01239                 if(final)
01240                 {
01241                         delete image_datap;
01242                 }
01243         }
01244 
01245         LLUIImageList* instance = getInstance();
01246 
01247         uuid_ui_image_map_t::iterator found_it = instance->mUIImages.find(ui_image_name);
01248         if (found_it != instance->mUIImages.end())
01249         {
01250                 LLUIImagePtr imagep = found_it->second;
01251 
01252                 // for images grabbed from local files, apply clipping rectangle to restore original dimensions
01253                 // from power-of-2 gl image
01254                 if (success && imagep.notNull() && src_vi && !src_vi->mLocalFileName.empty())
01255                 {
01256                         F32 clip_x = (F32)src_vi->getOriginalWidth() / (F32)src_vi->getWidth(0);
01257                         F32 clip_y = (F32)src_vi->getOriginalHeight() / (F32)src_vi->getHeight(0);
01258                         imagep->setClipRegion(LLRectf(0.f, clip_y, clip_x, 0.f));
01259                         if (scale_rect != LLRect::null)
01260                         {
01261                                 imagep->setScaleRegion(
01262                                         LLRectf(llclamp((F32)scale_rect.mLeft / (F32)imagep->getWidth(), 0.f, 1.f),
01263                                                 llclamp((F32)scale_rect.mTop / (F32)imagep->getHeight(), 0.f, 1.f),
01264                                                 llclamp((F32)scale_rect.mRight / (F32)imagep->getWidth(), 0.f, 1.f),
01265                                                 llclamp((F32)scale_rect.mBottom / (F32)imagep->getHeight(), 0.f, 1.f)));
01266                         }
01267                 }
01268         }
01269 }
01270 
01271 bool LLUIImageList::initFromFile(const LLString& filename)
01272 {
01273         LLXmlTree xml_tree;
01274 
01275         if (!xml_tree.parseFile(filename))
01276         {
01277                 llwarns << "Unable to parse UI image list file " << filename << llendl;
01278                 return false;
01279         }
01280 
01281         LLXmlTreeNode* rootp = xml_tree.getRoot();
01282         if (!rootp || !rootp->hasAttribute("version"))
01283         {
01284                 llwarns << "No valid version number in UI image list file " << filename << llendl;
01285                 return false;
01286         }
01287 
01288         enum
01289         {
01290                 PASS_DECODE_NOW,
01291                 PASS_DECODE_LATER,
01292                 NUM_PASSES
01293         };
01294 
01295         for (S32 pass = PASS_DECODE_NOW; pass < NUM_PASSES; pass++)
01296         {
01297                 LLXmlTreeNode* child_nodep = rootp->getFirstChild();
01298                 while(child_nodep)
01299                 {
01300                         LLString image_name = child_nodep->getName();
01301                         LLString file_name = image_name;
01302                         LLRect scale_rect;
01303                         BOOL use_mip_maps = FALSE;
01304 
01305                         BOOL preload = FALSE;
01306                         child_nodep->getAttributeBOOL("preload", preload);
01307 
01308                         // load high priority textures on first pass (to kick off decode)
01309                         if (preload)
01310                         {
01311                                 if (pass == PASS_DECODE_LATER) 
01312                                 {
01313                                         child_nodep = rootp->getNextChild();
01314                                         continue;
01315                                 }
01316                         }
01317                         else
01318                         {
01319                                 if (pass == PASS_DECODE_NOW)
01320                                 {
01321                                         child_nodep = rootp->getNextChild();
01322                                         continue;
01323                                 }
01324                         }
01325 
01326                         child_nodep->getAttributeString("file_name", file_name);
01327                         child_nodep->getAttributeBOOL("use_mips", use_mip_maps);
01328 
01329                         LLXmlTreeNode* rect_node = child_nodep->getChildByName("scale_rect");
01330                         if (rect_node)
01331                         {
01332                                 rect_node->getAttributeS32("left", scale_rect.mLeft);
01333                                 rect_node->getAttributeS32("right", scale_rect.mRight);
01334                                 rect_node->getAttributeS32("bottom", scale_rect.mBottom);
01335                                 rect_node->getAttributeS32("top", scale_rect.mTop);
01336                         }
01337                         
01338                         preloadUIImage(image_name, file_name, use_mip_maps, scale_rect);
01339                         
01340                         child_nodep = rootp->getNextChild();
01341                 }
01342 
01343                 if (pass == PASS_DECODE_NOW && !gSavedSettings.getBOOL("NoPreload"))
01344                 {
01345                         gImageList.decodeAllImages(2.f); // decode preloaded images
01346                 }
01347         }
01348         return true;
01349 }
01350 

Generated on Fri May 16 08:34:09 2008 for SecondLife by  doxygen 1.5.5