diff options
Diffstat (limited to 'indra/newview/llviewertexturelist.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/llviewertexturelist.cpp | 1030 |
1 files changed, 640 insertions, 390 deletions
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 10126219f8..926c40307b 100644..100755 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -30,7 +30,6 @@ #include "llviewertexturelist.h" -#include "imageids.h" #include "llgl.h" // fot gathering stats from GL #include "llimagegl.h" #include "llimagebmp.h" @@ -58,23 +57,19 @@ #include "pipeline.h" #include "llappviewer.h" #include "llxuiparser.h" - +#include "lltracerecording.h" +#include "llviewerdisplay.h" +#include "llviewerwindow.h" +#include "llprogressview.h" //////////////////////////////////////////////////////////////////////////// void (*LLViewerTextureList::sUUIDCallback)(void **, const LLUUID&) = NULL; -U32 LLViewerTextureList::sTextureBits = 0; -U32 LLViewerTextureList::sTexturePackets = 0; S32 LLViewerTextureList::sNumImages = 0; -LLStat LLViewerTextureList::sNumImagesStat(32, TRUE); -LLStat LLViewerTextureList::sNumRawImagesStat(32, TRUE); -LLStat LLViewerTextureList::sGLTexMemStat(32, TRUE); -LLStat LLViewerTextureList::sGLBoundMemStat(32, TRUE); -LLStat LLViewerTextureList::sRawMemStat(32, TRUE); -LLStat LLViewerTextureList::sFormattedMemStat(32, TRUE); LLViewerTextureList gTextureList; -static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images"); +static LLTrace::BlockTimerStatHandle FTM_PROCESS_IMAGES("Process Images"); + /////////////////////////////////////////////////////////////////////////////// @@ -82,25 +77,21 @@ LLViewerTextureList::LLViewerTextureList() : mForceResetTextureStats(FALSE), mUpdateStats(FALSE), mMaxResidentTexMemInMegaBytes(0), - mMaxTotalTextureMemInMegaBytes(0) + mMaxTotalTextureMemInMegaBytes(0), + mInitialized(FALSE) { } void LLViewerTextureList::init() -{ +{ + mInitialized = TRUE ; sNumImages = 0; - mMaxResidentTexMemInMegaBytes = 0; - mMaxTotalTextureMemInMegaBytes = 0 ; - if (gNoRender) - { - // Don't initialize GL stuff if we're not rendering. - return; - } - mUpdateStats = TRUE; + mMaxResidentTexMemInMegaBytes = (U32Bytes)0; + mMaxTotalTextureMemInMegaBytes = (U32Bytes)0; // Update how much texture RAM we're allowed to use. - updateMaxResidentTexMem(0); // 0 = use current + updateMaxResidentTexMem(S32Megabytes(0)); // 0 = use current doPreloadImages(); } @@ -110,14 +101,21 @@ void LLViewerTextureList::doPreloadImages() { LL_DEBUGS("ViewerImages") << "Preloading images..." << LL_ENDL; + llassert_always(mInitialized) ; + llassert_always(mImageList.empty()) ; + llassert_always(mUUIDMap.empty()) ; + // Set the "missing asset" image - LLViewerFetchedTexture::sMissingAssetImagep = LLViewerTextureManager::getFetchedTextureFromFile("missing_asset.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); + LLViewerFetchedTexture::sMissingAssetImagep = LLViewerTextureManager::getFetchedTextureFromFile("missing_asset.tga", FTT_LOCAL_FILE, MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); // Set the "white" image - LLViewerFetchedTexture::sWhiteImagep = LLViewerTextureManager::getFetchedTextureFromFile("white.tga", MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); - + LLViewerFetchedTexture::sWhiteImagep = LLViewerTextureManager::getFetchedTextureFromFile("white.tga", FTT_LOCAL_FILE, MIPMAP_NO, LLViewerFetchedTexture::BOOST_UI); + LLTexUnit::sWhiteTexture = LLViewerFetchedTexture::sWhiteImagep->getTexName(); LLUIImageList* image_list = LLUIImageList::getInstance(); + // Set the default flat normal map + LLViewerFetchedTexture::sFlatNormalImagep = LLViewerTextureManager::getFetchedTextureFromFile("flatnormal.tga", FTT_LOCAL_FILE, MIPMAP_NO, LLViewerFetchedTexture::BOOST_BUMP); + image_list->initFromFile(); // turn off clamping and bilinear filtering for uv picking images @@ -129,33 +127,33 @@ void LLViewerTextureList::doPreloadImages() //uv_test->setMipFilterNearest(TRUE, TRUE); // prefetch specific UUIDs - LLViewerTextureManager::getFetchedTexture(IMG_SHOT, TRUE); - LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF, TRUE); - LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTextureFromFile("silhouette.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); + LLViewerTextureManager::getFetchedTexture(IMG_SHOT); + LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF); + LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTextureFromFile("silhouette.j2c", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } - image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryLines.png", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); + image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryLines.png", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } - image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); + image = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } - image = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); + image = LLViewerTextureManager::getFetchedTexture(DEFAULT_WATER_NORMAL, FTT_DEFAULT, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI); if (image) { image->setAddressMode(LLTexUnit::TAM_WRAP); mImagePreloads.insert(image); } - image = LLViewerTextureManager::getFetchedTextureFromFile("transparent.j2c", MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI, LLViewerTexture::FETCHED_TEXTURE, + image = LLViewerTextureManager::getFetchedTextureFromFile("transparent.j2c", FTT_LOCAL_FILE, MIPMAP_YES, LLViewerFetchedTexture::BOOST_UI, LLViewerTexture::FETCHED_TEXTURE, 0,0,LLUUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903")); if (image) { @@ -167,7 +165,7 @@ void LLViewerTextureList::doPreloadImages() static std::string get_texture_list_name() { - return std::string("texture_list_") + gSavedSettings.getString("LoginLocation") + ".xml"; + return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "texture_list_" + gSavedSettings.getString("LoginLocation") + ".xml"); } void LLViewerTextureList::doPrefetchImages() @@ -180,13 +178,22 @@ void LLViewerTextureList::doPrefetchImages() // Pre-fetch textures from last logout LLSD imagelist; - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); + std::string filename = get_texture_list_name(); llifstream file; - file.open(filename); + file.open(filename.c_str()); if (file.is_open()) { - LLSDSerialize::fromXML(imagelist, file); - } + if ( ! LLSDSerialize::fromXML(imagelist, file) ) + { + file.close(); + LL_WARNS() << "XML parse error reading texture list '" << filename << "'" << LL_ENDL; + LL_WARNS() << "Removing invalid texture list '" << filename << "'" << LL_ENDL; + LLFile::remove(filename); + return; + } + file.close(); + } + S32 texture_count = 0; for (LLSD::array_iterator iter = imagelist.beginArray(); iter != imagelist.endArray(); ++iter) { @@ -197,13 +204,15 @@ void LLViewerTextureList::doPrefetchImages() if(LLViewerTexture::FETCHED_TEXTURE == texture_type || LLViewerTexture::LOD_TEXTURE == texture_type) { - LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(uuid, MIPMAP_TRUE, LLViewerTexture::BOOST_NONE, texture_type); + LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(uuid, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, texture_type); if (image) { + texture_count += 1; image->addTextureStats((F32)pixel_area); } } } + LL_DEBUGS() << "fetched " << texture_count << " images from " << filename << LL_ENDL; } /////////////////////////////////////////////////////////////////////////////// @@ -227,7 +236,9 @@ void LLViewerTextureList::shutdown() if (!image->hasGLTexture() || !image->getUseDiscard() || image->needsAux() || - image->getTargetHost() != LLHost::invalid) + image->getTargetHost() != LLHost::invalid || + !image->getUrl().empty() + ) { continue; // avoid UI, baked, and other special images } @@ -261,9 +272,10 @@ void LLViewerTextureList::shutdown() if (count > 0 && !gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "").empty()) { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, get_texture_list_name()); + std::string filename = get_texture_list_name(); llofstream file; - file.open(filename); + file.open(filename.c_str()); + LL_DEBUGS() << "saving " << imagelist.size() << " image list entries" << LL_ENDL; LLSDSerialize::toPrettyXML(imagelist, file); } @@ -275,26 +287,29 @@ void LLViewerTextureList::shutdown() // Flush all of the references mLoadingStreamList.clear(); mCreateTextureList.clear(); + mFastCacheList.clear(); mUUIDMap.clear(); mImageList.clear(); + + mInitialized = FALSE ; //prevent loading textures again. } void LLViewerTextureList::dump() { - llinfos << "LLViewerTextureList::dump()" << llendl; + LL_INFOS() << "LLViewerTextureList::dump()" << LL_ENDL; for (image_priority_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it) { LLViewerFetchedTexture* image = *it; - llinfos << "priority " << image->getDecodePriority() + LL_INFOS() << "priority " << image->getDecodePriority() << " boost " << image->getBoostLevel() << " size " << image->getWidth() << "x" << image->getHeight() << " discard " << image->getDiscardLevel() << " desired " << image->getDesiredDiscardLevel() << " http://asset.siva.lindenlab.com/" << image->getID() << ".texture" - << llendl; + << LL_ENDL; } } @@ -305,6 +320,7 @@ void LLViewerTextureList::destroyGL(BOOL save_state) void LLViewerTextureList::restoreGL() { + llassert_always(mInitialized) ; LLImageGL::restoreGL(); } @@ -317,7 +333,8 @@ void LLViewerTextureList::restoreGL() /////////////////////////////////////////////////////////////////////////////// -LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename, +LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& filename, + FTType f_type, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -325,19 +342,25 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& LLGLenum primary_format, const LLUUID& force_id) { + if(!mInitialized) + { + return NULL ; + } + std::string full_path = gDirUtilp->findSkinnedFilename("textures", filename); if (full_path.empty()) { - llwarns << "Failed to find local image file: " << filename << llendl; - return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); + LL_WARNS() << "Failed to find local image file: " << filename << LL_ENDL; + return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI); } std::string url = "file://" + full_path; - return getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); + return getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); } LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url, + FTType f_type, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -345,11 +368,9 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& LLGLenum primary_format, const LLUUID& force_id) { - if (gNoRender) + if(!mInitialized) { - // Never mind that this ignores image_set_id; - // getImage() will handle that later. - return LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI); + return NULL ; } // generate UUID based on hash of filename @@ -364,19 +385,36 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& } LLPointer<LLViewerFetchedTexture> imagep = findImage(new_id); - + + if (!imagep.isNull()) + { + LLViewerFetchedTexture *texture = imagep.get(); + if (texture->getUrl().empty()) + { + LL_WARNS() << "Requested texture " << new_id << " already exists but does not have a URL" << LL_ENDL; + } + else if (texture->getUrl() != url) + { + // This is not an error as long as the images really match - + // e.g. could be two avatars wearing the same outfit. + LL_DEBUGS("Avatar") << "Requested texture " << new_id + << " already exists with a different url, requested: " << url + << " current: " << texture->getUrl() << LL_ENDL; + } + + } if (imagep.isNull()) { switch(texture_type) { case LLViewerTexture::FETCHED_TEXTURE: - imagep = new LLViewerFetchedTexture(url, new_id, usemipmaps); + imagep = new LLViewerFetchedTexture(url, f_type, new_id, usemipmaps); break ; case LLViewerTexture::LOD_TEXTURE: - imagep = new LLViewerLODTexture(url, new_id, usemipmaps); + imagep = new LLViewerLODTexture(url, f_type, new_id, usemipmaps); break ; default: - llerrs << "Invalid texture type " << texture_type << llendl ; + LL_ERRS() << "Invalid texture type " << texture_type << LL_ENDL ; } if (internal_format && primary_format) @@ -403,7 +441,8 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& } -LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, +LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, + FTType f_type, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -411,20 +450,45 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, LLGLenum primary_format, LLHost request_from_host) { + if(!mInitialized) + { + return NULL ; + } + // Return the image with ID image_id // If the image is not found, creates new image and // enqueues a request for transmission - if ((&image_id == NULL) || image_id.isNull()) + if (image_id.isNull()) { - return (LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI)); + return (LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI)); } LLPointer<LLViewerFetchedTexture> imagep = findImage(image_id); - + if (!imagep.isNull()) + { + LLViewerFetchedTexture *texture = imagep.get(); + if (request_from_host.isOk() && + !texture->getTargetHost().isOk()) + { + LL_WARNS() << "Requested texture " << image_id << " already exists but does not have a host" << LL_ENDL; + } + else if (request_from_host.isOk() && + texture->getTargetHost().isOk() && + request_from_host != texture->getTargetHost()) + { + LL_WARNS() << "Requested texture " << image_id << " already exists with a different target host, requested: " + << request_from_host << " current: " << texture->getTargetHost() << LL_ENDL; + } + if (f_type != FTT_DEFAULT && imagep->getFTType() != f_type) + { + LL_WARNS() << "FTType mismatch: requested " << f_type << " image has " << imagep->getFTType() << LL_ENDL; + } + + } if (imagep.isNull()) { - imagep = createImage(image_id, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ; + imagep = createImage(image_id, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ; } imagep->setGLTextureCreated(true); @@ -433,7 +497,8 @@ LLViewerFetchedTexture* LLViewerTextureList::getImage(const LLUUID &image_id, } //when this function is called, there is no such texture in the gTextureList with image_id. -LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, +LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, + FTType f_type, BOOL usemipmaps, LLViewerTexture::EBoostLevel boost_priority, S8 texture_type, @@ -441,17 +506,19 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, LLGLenum primary_format, LLHost request_from_host) { + static LLCachedControl<bool> fast_cache_fetching_enabled(gSavedSettings, "FastCacheFetchEnabled", true); + LLPointer<LLViewerFetchedTexture> imagep ; switch(texture_type) { case LLViewerTexture::FETCHED_TEXTURE: - imagep = new LLViewerFetchedTexture(image_id, request_from_host, usemipmaps); + imagep = new LLViewerFetchedTexture(image_id, f_type, request_from_host, usemipmaps); break ; case LLViewerTexture::LOD_TEXTURE: - imagep = new LLViewerLODTexture(image_id, request_from_host, usemipmaps); + imagep = new LLViewerLODTexture(image_id, f_type, request_from_host, usemipmaps); break ; default: - llerrs << "Invalid texture type " << texture_type << llendl ; + LL_ERRS() << "Invalid texture type " << texture_type << LL_ENDL ; } if (internal_format && primary_format) @@ -478,6 +545,11 @@ LLViewerFetchedTexture* LLViewerTextureList::createImage(const LLUUID &image_id, imagep->forceActive() ; } + if(fast_cache_fetching_enabled) + { + mFastCacheList.insert(imagep); + imagep->setInFastCacheList(true); + } return imagep ; } @@ -491,35 +563,66 @@ LLViewerFetchedTexture *LLViewerTextureList::findImage(const LLUUID &image_id) void LLViewerTextureList::addImageToList(LLViewerFetchedTexture *image) { + assert_main_thread(); + llassert_always(mInitialized) ; llassert(image); if (image->isInImageList()) - { - llerrs << "LLViewerTextureList::addImageToList - Image already in list" << llendl; + { // Flag is already set? + LL_WARNS() << "LLViewerTextureList::addImageToList - image " << image->getID() << " already in list" << LL_ENDL; } + else + { if((mImageList.insert(image)).second != true) { - llerrs << "Error happens when insert image to mImageList!" << llendl ; + LL_WARNS() << "Error happens when insert image " << image->getID() << " into mImageList!" << LL_ENDL ; } - image->setInImageList(TRUE) ; } +} void LLViewerTextureList::removeImageFromList(LLViewerFetchedTexture *image) { + assert_main_thread(); + llassert_always(mInitialized) ; llassert(image); - if (!image->isInImageList()) + + S32 count = 0; + if (image->isInImageList()) + { + count = mImageList.erase(image) ; + if(count != 1) { - llinfos << "RefCount: " << image->getNumRefs() << llendl ; + LL_INFOS() << "Image " << image->getID() + << " had mInImageList set but mImageList.erase() returned " << count + << LL_ENDL; + } + } + else + { // Something is wrong, image is expected in list or callers should check first + LL_INFOS() << "Calling removeImageFromList() for " << image->getID() + << " but doesn't have mInImageList set" + << " ref count is " << image->getNumRefs() + << LL_ENDL; uuid_map_t::iterator iter = mUUIDMap.find(image->getID()); - if(iter == mUUIDMap.end() || iter->second != image) + if(iter == mUUIDMap.end()) { - llinfos << "Image is not in mUUIDMap!" << llendl ; + LL_INFOS() << "Image " << image->getID() << " is also not in mUUIDMap!" << LL_ENDL ; } - llerrs << "LLViewerTextureList::removeImageFromList - Image not in list" << llendl; + else if (iter->second != image) + { + LL_INFOS() << "Image " << image->getID() << " was in mUUIDMap but with different pointer" << LL_ENDL ; } - if(mImageList.erase(image) != 1) + else { - llerrs << "Error happens when remove image from mImageList!" << llendl ; + LL_INFOS() << "Image " << image->getID() << " was in mUUIDMap with same pointer" << LL_ENDL ; + } + count = mImageList.erase(image) ; + if(count != 0) + { // it was in the list already? + LL_WARNS() << "Image " << image->getID() + << " had mInImageList false but mImageList.erase() returned " << count + << LL_ENDL; + } } image->setInImageList(FALSE) ; @@ -529,15 +632,15 @@ void LLViewerTextureList::addImage(LLViewerFetchedTexture *new_image) { if (!new_image) { - llwarning("No image to add to image list", 0); return; } + //llassert(new_image); LLUUID image_id = new_image->getID(); LLViewerFetchedTexture *image = findImage(image_id); if (image) { - llwarns << "Image with ID " << image_id << " already in list" << llendl; + LL_INFOS() << "Image with ID " << image_id << " already in list" << LL_ENDL; } sNumImages++; @@ -572,61 +675,134 @@ void LLViewerTextureList::dirtyImage(LLViewerFetchedTexture *image) } //////////////////////////////////////////////////////////////////////////// -static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_MARK_DIRTY("Dirty Images"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_UPDATE_PRIORITIES("Prioritize"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_CALLBACKS("Callbacks"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_FETCH("Fetch"); +static LLTrace::BlockTimerStatHandle FTM_FAST_CACHE_IMAGE_FETCH("Fast Cache Fetch"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_CREATE("Create"); +static LLTrace::BlockTimerStatHandle FTM_IMAGE_STATS("Stats"); +static LLTrace::BlockTimerStatHandle FTM_UPDATE_TEXTURES("Update Textures"); void LLViewerTextureList::updateImages(F32 max_time) { - LLAppViewer::getTextureFetch()->setTextureBandwidth(LLViewerStats::getInstance()->mTextureKBitStat.getMeanPerSec()); - - LLViewerStats::getInstance()->mNumImagesStat.addValue(sNumImages); - LLViewerStats::getInstance()->mNumRawImagesStat.addValue(LLImageRaw::sRawImageCount); - LLViewerStats::getInstance()->mGLTexMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sGlobalTextureMemoryInBytes)); - LLViewerStats::getInstance()->mGLBoundMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageGL::sBoundTextureMemoryInBytes)); - LLViewerStats::getInstance()->mRawMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageRaw::sGlobalRawMemory)); - LLViewerStats::getInstance()->mFormattedMemStat.addValue((F32)BYTES_TO_MEGA_BYTES(LLImageFormatted::sGlobalFormattedMemory)); - - updateImagesDecodePriorities(); + LL_RECORD_BLOCK_TIME(FTM_UPDATE_TEXTURES); + static BOOL cleared = FALSE; + if(gTeleportDisplay) + { + if(!cleared) + { + clearFetchingRequests(); + gPipeline.clearRebuildGroups(); + cleared = TRUE; + } + return; + } + cleared = FALSE; + + LLAppViewer::getTextureFetch()->setTextureBandwidth(LLTrace::get_frame_recording().getPeriodMeanPerSec(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED).value()); + + { + using namespace LLStatViewer; + sample(NUM_IMAGES, sNumImages); + sample(NUM_RAW_IMAGES, LLImageRaw::sRawImageCount); + sample(GL_TEX_MEM, LLImageGL::sGlobalTextureMemory); + sample(GL_BOUND_MEM, LLImageGL::sBoundTextureMemory); + sample(RAW_MEM, F64Bytes(LLImageRaw::sGlobalRawMemory)); + sample(FORMATTED_MEM, F64Bytes(LLImageFormatted::sGlobalFormattedMemory)); + } + + { + //loading from fast cache + LL_RECORD_BLOCK_TIME(FTM_FAST_CACHE_IMAGE_FETCH); + max_time -= updateImagesLoadingFastCache(max_time); + } + + { + LL_RECORD_BLOCK_TIME(FTM_IMAGE_UPDATE_PRIORITIES); + updateImagesDecodePriorities(); + } F32 total_max_time = max_time; - max_time -= updateImagesFetchTextures(max_time); + + { + LL_RECORD_BLOCK_TIME(FTM_IMAGE_FETCH); + max_time -= updateImagesFetchTextures(max_time); + } - max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time - max_time -= updateImagesCreateTextures(max_time); + { + LL_RECORD_BLOCK_TIME(FTM_IMAGE_CREATE); + max_time = llmax(max_time, total_max_time*.50f); // at least 50% of max_time + max_time -= updateImagesCreateTextures(max_time); + } if (!mDirtyTextureList.empty()) { - LLFastTimer t(FTM_IMAGE_MARK_DIRTY); + LL_RECORD_BLOCK_TIME(FTM_IMAGE_MARK_DIRTY); gPipeline.dirtyPoolObjectTextures(mDirtyTextureList); mDirtyTextureList.clear(); } - bool didone = false; - for (image_list_t::iterator iter = mCallbackList.begin(); - iter != mCallbackList.end(); ) + { - //trigger loaded callbacks on local textures immediately - LLViewerFetchedTexture* image = *iter++; - if (!image->getUrl().empty()) + LL_RECORD_BLOCK_TIME(FTM_IMAGE_CALLBACKS); + bool didone = false; + for (image_list_t::iterator iter = mCallbackList.begin(); + iter != mCallbackList.end(); ) { - // Do stuff to handle callbacks, update priorities, etc. - didone = image->doLoadedCallbacks(); - } - else if (!didone) - { - // Do stuff to handle callbacks, update priorities, etc. - didone = image->doLoadedCallbacks(); + //trigger loaded callbacks on local textures immediately + LLViewerFetchedTexture* image = *iter++; + if (!image->getUrl().empty()) + { + // Do stuff to handle callbacks, update priorities, etc. + didone = image->doLoadedCallbacks(); + } + else if (!didone) + { + // Do stuff to handle callbacks, update priorities, etc. + didone = image->doLoadedCallbacks(); + } } } - updateImagesUpdateStats(); + + { + LL_RECORD_BLOCK_TIME(FTM_IMAGE_STATS); + updateImagesUpdateStats(); + } +} + +void LLViewerTextureList::clearFetchingRequests() +{ + if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) + { + return; + } + + LLAppViewer::getTextureFetch()->deleteAllRequests(); + + for (image_priority_list_t::iterator iter = mImageList.begin(); + iter != mImageList.end(); ++iter) + { + LLViewerFetchedTexture* imagep = *iter; + imagep->forceToDeleteRequest() ; + } } void LLViewerTextureList::updateImagesDecodePriorities() { // Update the decode priority for N images each frame { - const size_t max_update_count = llmin((S32) (1024*gFrameIntervalSeconds) + 1, 32); //target 1024 textures per second - S32 update_counter = llmin(max_update_count, mUUIDMap.size()/10); + F32 lazy_flush_timeout = 30.f; // stop decoding + F32 max_inactive_time = 20.f; // actually delete + S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference + + //reset imagep->getLastReferencedTimer() when screen is showing the progress view to avoid removing pre-fetched textures too soon. + bool reset_timer = gViewerWindow->getProgressView()->getVisible(); + + static const S32 MAX_PRIO_UPDATES = gSavedSettings.getS32("TextureFetchUpdatePriorities"); // default: 32 + const size_t max_update_count = llmin((S32) (MAX_PRIO_UPDATES*MAX_PRIO_UPDATES*gFrameIntervalSeconds.value()) + 1, MAX_PRIO_UPDATES); + S32 update_counter = llmin(max_update_count, mUUIDMap.size()); uuid_map_t::iterator iter = mUUIDMap.upper_bound(mLastUpdateUUID); - while(update_counter > 0 && !mUUIDMap.empty()) + while ((update_counter-- > 0) && !mUUIDMap.empty()) { if (iter == mUUIDMap.end()) { @@ -634,22 +810,25 @@ void LLViewerTextureList::updateImagesDecodePriorities() } mLastUpdateUUID = iter->first; LLPointer<LLViewerFetchedTexture> imagep = iter->second; - ++iter; // safe to incrament now + ++iter; // safe to increment now + + if(imagep->isInDebug()) + { + update_counter--; + continue; //is in debug, ignore. + } // // Flush formatted images using a lazy flush // - const F32 LAZY_FLUSH_TIMEOUT = 30.f; // stop decoding - const F32 MAX_INACTIVE_TIME = 50.f; // actually delete - S32 min_refs = 3; // 1 for mImageList, 1 for mUUIDMap, 1 for local reference - if (imagep->hasCallbacks()) - { - min_refs++; // Add an extra reference if we're on the loaded callback list - } S32 num_refs = imagep->getNumRefs(); if (num_refs == min_refs) { - if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > LAZY_FLUSH_TIMEOUT) + if(reset_timer) + { + imagep->getLastReferencedTimer()->reset(); + } + else if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > lazy_flush_timeout) { // Remove the unused image from the image list deleteImage(imagep); @@ -661,7 +840,7 @@ void LLViewerTextureList::updateImagesDecodePriorities() { if(imagep->hasSavedRawImage()) { - if(imagep->getElapsedLastReferencedSavedRawImageTime() > MAX_INACTIVE_TIME) + if(imagep->getElapsedLastReferencedSavedRawImageTime() > max_inactive_time) { imagep->destroySavedRawImage() ; } @@ -678,7 +857,11 @@ void LLViewerTextureList::updateImagesDecodePriorities() } else if(imagep->isInactive()) { - if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME) + if(reset_timer) + { + imagep->getLastReferencedTimer()->reset(); + } + else if (imagep->getLastReferencedTimer()->getElapsedTimeF32() > max_inactive_time) { imagep->setDeletionCandidate() ; } @@ -692,7 +875,16 @@ void LLViewerTextureList::updateImagesDecodePriorities() imagep->setInactive() ; } } - + + if (!imagep->isInImageList()) + { + continue; + } + if(imagep->isInFastCacheList()) + { + continue; //wait for loading from the fast cache. + } + imagep->processTextureStats(); F32 old_priority = imagep->getDecodePriority(); F32 old_priority_test = llmax(old_priority, 0.0f); @@ -702,15 +894,35 @@ void LLViewerTextureList::updateImagesDecodePriorities() if ((decode_priority_test < old_priority_test * .8f) || (decode_priority_test > old_priority_test * 1.25f)) { - removeImageFromList(imagep); + mImageList.erase(imagep) ; imagep->setDecodePriority(decode_priority); - addImageToList(imagep); + mImageList.insert(imagep); } - update_counter--; } } } +void LLViewerTextureList::setDebugFetching(LLViewerFetchedTexture* tex, S32 debug_level) +{ + if(!tex->setDebugFetching(debug_level)) + { + return; + } + + const F32 DEBUG_PRIORITY = 100000.f; + F32 old_priority_test = llmax(tex->getDecodePriority(), 0.0f); + F32 decode_priority_test = DEBUG_PRIORITY; + + // Ignore < 20% difference + if ((decode_priority_test < old_priority_test * .8f) || + (decode_priority_test > old_priority_test * 1.25f)) + { + removeImageFromList(tex); + tex->setDecodePriority(decode_priority_test); + addImageToList(tex); + } +} + /* static U8 get_image_type(LLViewerFetchedTexture* imagep, LLHost target_host) { @@ -727,28 +939,26 @@ void LLViewerTextureList::updateImagesDecodePriorities() if (type_from_host == LLImageBase::TYPE_NORMAL && type_from_boost == LLImageBase::TYPE_AVATAR_BAKE) { - llwarns << "TAT: get_image_type() type_from_host doesn't match type_from_boost" + LL_WARNS() << "TAT: get_image_type() type_from_host doesn't match type_from_boost" << " host " << target_host << " boost " << imagep->getBoostLevel() << " imageid " << imagep->getID() - << llendl; + << LL_ENDL; imagep->dump(); } return type_from_host; } */ -static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create Images"); F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) { - if (gNoRender || gGLManager.mIsDisabled) return 0.0f; + if (gGLManager.mIsDisabled) return 0.0f; // // Create GL textures for all textures that need them (images which have been // decoded, but haven't been pushed into GL). // - LLFastTimer t(FTM_IMAGE_CREATE); - + LLTimer create_timer; image_list_t::iterator enditer = mCreateTextureList.begin(); for (image_list_t::iterator iter = mCreateTextureList.begin(); @@ -767,6 +977,36 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } +F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time) +{ + if (gGLManager.mIsDisabled) return 0.0f; + if(mFastCacheList.empty()) + { + return 0.f; + } + + // + // loading texture raw data from the fast cache directly. + // + + LLTimer timer; + image_list_t::iterator enditer = mFastCacheList.begin(); + for (image_list_t::iterator iter = mFastCacheList.begin(); + iter != mFastCacheList.end();) + { + image_list_t::iterator curiter = iter++; + enditer = iter; + LLViewerFetchedTexture *imagep = *curiter; + imagep->loadFromFastCache(); + if (timer.getElapsedTimeF32() > max_time) + { + break; + } + } + mFastCacheList.erase(mFastCacheList.begin(), enditer); + return timer.getElapsedTimeF32(); +} + void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) { if(!imagep) @@ -781,9 +1021,8 @@ void LLViewerTextureList::forceImmediateUpdate(LLViewerFetchedTexture* imagep) imagep->processTextureStats(); F32 decode_priority = LLViewerFetchedTexture::maxDecodePriority() ; imagep->setDecodePriority(decode_priority); - mImageList.insert(imagep); - imagep->setInImageList(TRUE) ; - + addImageToList(imagep); + return ; } @@ -791,15 +1030,24 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) { LLTimer image_op_timer; - // Update the decode priority for N images each frame - // Make a list with 32 high priority entries + 256 cycled entries - const size_t max_priority_count = llmin((S32) (256*10.f*gFrameIntervalSeconds)+1, 32); - const size_t max_update_count = llmin((S32) (1024*10.f*gFrameIntervalSeconds)+1, 256); + // Update fetch for N images each frame + static const S32 MAX_HIGH_PRIO_COUNT = gSavedSettings.getS32("TextureFetchUpdateHighPriority"); // default: 32 + static const S32 MAX_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMaxMediumPriority"); // default: 256 + static const S32 MIN_UPDATE_COUNT = gSavedSettings.getS32("TextureFetchUpdateMinMediumPriority"); // default: 32 + static const F32 MIN_PRIORITY_THRESHOLD = gSavedSettings.getF32("TextureFetchUpdatePriorityThreshold"); // default: 0.0 + static const bool SKIP_LOW_PRIO = gSavedSettings.getBOOL("TextureFetchUpdateSkipLowPriority"); // default: false + + size_t max_priority_count = llmin((S32) (MAX_HIGH_PRIO_COUNT*MAX_HIGH_PRIO_COUNT*gFrameIntervalSeconds.value())+1, MAX_HIGH_PRIO_COUNT); + max_priority_count = llmin(max_priority_count, mImageList.size()); + + size_t total_update_count = mUUIDMap.size(); + size_t max_update_count = llmin((S32) (MAX_UPDATE_COUNT*MAX_UPDATE_COUNT*gFrameIntervalSeconds.value())+1, MAX_UPDATE_COUNT); + max_update_count = llmin(max_update_count, total_update_count); - // 32 high priority entries + // MAX_HIGH_PRIO_COUNT high priority entries typedef std::vector<LLViewerFetchedTexture*> entries_list_t; entries_list_t entries; - size_t update_counter = llmin(max_priority_count, mImageList.size()); + size_t update_counter = max_priority_count; image_priority_list_t::iterator iter1 = mImageList.begin(); while(update_counter > 0) { @@ -809,52 +1057,47 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) update_counter--; } - // 256 cycled entries - update_counter = llmin(max_update_count, mUUIDMap.size()); + // MAX_UPDATE_COUNT cycled entries + update_counter = max_update_count; if(update_counter > 0) { uuid_map_t::iterator iter2 = mUUIDMap.upper_bound(mLastFetchUUID); - uuid_map_t::iterator iter2p = iter2; - while(update_counter > 0) + while ((update_counter > 0) && (total_update_count > 0)) { if (iter2 == mUUIDMap.end()) { iter2 = mUUIDMap.begin(); } - entries.push_back(iter2->second); - iter2p = iter2++; - update_counter--; + LLViewerFetchedTexture* imagep = iter2->second; + // Skip the textures where there's really nothing to do so to give some times to others. Also skip the texture if it's already in the high prio set. + if (!SKIP_LOW_PRIO || (SKIP_LOW_PRIO && ((imagep->getDecodePriority() > MIN_PRIORITY_THRESHOLD) || imagep->hasFetcher()))) + { + entries.push_back(imagep); + update_counter--; + } + + iter2++; + total_update_count--; } - - mLastFetchUUID = iter2p->first; } S32 fetch_count = 0; - S32 min_count = max_priority_count + max_update_count/4; + size_t min_update_count = llmin(MIN_UPDATE_COUNT,(S32)(entries.size()-max_priority_count)); + S32 min_count = max_priority_count + min_update_count; for (entries_list_t::iterator iter3 = entries.begin(); iter3 != entries.end(); ) { - LLPointer<LLViewerFetchedTexture> imagep = *iter3++; - - bool fetching = imagep->updateFetch(); - if (fetching) + LLViewerFetchedTexture* imagep = *iter3++; + fetch_count += (imagep->updateFetch() ? 1 : 0); + if (min_count <= min_update_count) { - fetch_count++; + mLastFetchUUID = imagep->getID(); } - if (min_count <= 0 && image_op_timer.getElapsedTimeF32() > max_time) + if ((min_count-- <= 0) && (image_op_timer.getElapsedTimeF32() > max_time)) { break; } - min_count--; - } - //if (fetch_count == 0) - //{ - // gDebugTimers[0].pause(); - //} - //else - //{ - // gDebugTimers[0].unpause(); - //} + } return image_op_timer.getElapsedTimeF32(); } @@ -876,8 +1119,10 @@ void LLViewerTextureList::updateImagesUpdateStats() void LLViewerTextureList::decodeAllImages(F32 max_time) { LLTimer timer; - if(gNoRender) return; - + + //loading from fast cache + updateImagesLoadingFastCache(max_time); + // Update texture stats and priorities std::vector<LLPointer<LLViewerFetchedTexture> > image_list; for (image_priority_list_t::iterator iter = mImageList.begin(); @@ -887,6 +1132,8 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) image_list.push_back(imagep); imagep->setInImageList(FALSE) ; } + + llassert_always(image_list.size() == mImageList.size()) ; mImageList.clear(); for (std::vector<LLPointer<LLViewerFetchedTexture> >::iterator iter = image_list.begin(); iter != image_list.end(); ++iter) @@ -895,8 +1142,7 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) imagep->processTextureStats(); F32 decode_priority = imagep->calcDecodePriority(); imagep->setDecodePriority(decode_priority); - mImageList.insert(imagep); - imagep->setInImageList(TRUE) ; + addImageToList(imagep); } image_list.clear(); @@ -940,99 +1186,54 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec) -{ - // First, load the image. +{ + // Load the image + LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec); + if (image.isNull()) + { + image->setLastError("Couldn't open the image to be uploaded."); + return FALSE; + } + if (!image->load(filename)) + { + image->setLastError("Couldn't load the image to be uploaded."); + return FALSE; + } + // Decompress or expand it in a raw image structure LLPointer<LLImageRaw> raw_image = new LLImageRaw; - - switch (codec) + if (!image->decode(raw_image, 0.0f)) { - case IMG_CODEC_BMP: - { - LLPointer<LLImageBMP> bmp_image = new LLImageBMP; - - if (!bmp_image->load(filename)) - { - return FALSE; - } - - if (!bmp_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - case IMG_CODEC_TGA: - { - LLPointer<LLImageTGA> tga_image = new LLImageTGA; - - if (!tga_image->load(filename)) - { - return FALSE; - } - - if (!tga_image->decode(raw_image)) - { - return FALSE; - } - - if( (tga_image->getComponents() != 3) && - (tga_image->getComponents() != 4) ) - { - tga_image->setLastError( "Image files with less than 3 or more than 4 components are not supported." ); - return FALSE; - } - } - break; - case IMG_CODEC_JPEG: - { - LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG; - - if (!jpeg_image->load(filename)) - { - return FALSE; - } - - if (!jpeg_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - case IMG_CODEC_PNG: - { - LLPointer<LLImagePNG> png_image = new LLImagePNG; - - if (!png_image->load(filename)) - { - return FALSE; - } - - if (!png_image->decode(raw_image, 0.0f)) - { - return FALSE; - } - } - break; - default: - return FALSE; + image->setLastError("Couldn't decode the image to be uploaded."); + return FALSE; } - - LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image); - - if( !compressedImage->save(out_filename) ) + // Check the image constraints + if ((image->getComponents() != 3) && (image->getComponents() != 4)) { - llinfos << "Couldn't create output file " << out_filename << llendl; + image->setLastError("Image files with less than 3 or more than 4 components are not supported."); return FALSE; } - - // test to see if the encode and save worked. + // Convert to j2c (JPEG2000) and save the file locally + LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image); + if (compressedImage.isNull()) + { + image->setLastError("Couldn't convert the image to jpeg2000."); + LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL; + return FALSE; + } + if (!compressedImage->save(out_filename)) + { + image->setLastError("Couldn't create the jpeg2000 image for upload."); + LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL; + return FALSE; + } + // Test to see if the encode and save worked LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C; - if( !integrity_test->loadAndValidate( out_filename ) ) + if (!integrity_test->loadAndValidate( out_filename )) { - llinfos << "Image: " << out_filename << " is corrupt." << llendl; + image->setLastError("The created jpeg2000 image is corrupt."); + LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL; return FALSE; } - return TRUE; } @@ -1041,38 +1242,59 @@ LLPointer<LLImageJ2C> LLViewerTextureList::convertToUploadFile(LLPointer<LLImage { raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C(); - compressedImage->setRate(0.f); if (gSavedSettings.getBOOL("LosslessJ2CUpload") && (raw_image->getWidth() * raw_image->getHeight() <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF)) compressedImage->setReversible(TRUE); - compressedImage->encode(raw_image, 0.0f); + + if (gSavedSettings.getBOOL("Jpeg2000AdvancedCompression")) + { + // This test option will create jpeg2000 images with precincts for each level, RPCL ordering + // and PLT markers. The block size is also optionally modifiable. + // Note: the images hence created are compatible with older versions of the viewer. + // Read the blocks and precincts size settings + S32 block_size = gSavedSettings.getS32("Jpeg2000BlocksSize"); + S32 precinct_size = gSavedSettings.getS32("Jpeg2000PrecinctsSize"); + LL_INFOS() << "Advanced JPEG2000 Compression: precinct = " << precinct_size << ", block = " << block_size << LL_ENDL; + compressedImage->initEncode(*raw_image, block_size, precinct_size, 0); + } + + if (!compressedImage->encode(raw_image, 0.0f)) + { + LL_INFOS() << "convertToUploadFile : encode returns with error!!" << LL_ENDL; + // Clear up the pointer so we don't leak that one + compressedImage = NULL; + } return compressedImage; } -const S32 MIN_VIDEO_RAM = 32; -const S32 MAX_VIDEO_RAM = 512; // 512MB max for performance reasons. - // Returns min setting for TextureMemory (in MB) -S32 LLViewerTextureList::getMinVideoRamSetting() +S32Megabytes LLViewerTextureList::getMinVideoRamSetting() { - S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); + S32Megabytes system_ram = gSysMemory.getPhysicalMemoryClamped(); //min texture mem sets to 64M if total physical mem is more than 1.5GB - return (system_ram > 1500) ? 64 : MIN_VIDEO_RAM_IN_MEGA_BYTES ; + return (system_ram > S32Megabytes(1500)) ? S32Megabytes(64) : gMinVideoRam ; } //static // Returns max setting for TextureMemory (in MB) -S32 LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended) +S32Megabytes LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended, float mem_multiplier) { - S32 max_texmem; + S32Megabytes max_texmem; if (gGLManager.mVRAM != 0) { // Treat any card with < 32 MB (shudder) as having 32 MB // - it's going to be swapping constantly regardless - S32 max_vram = gGLManager.mVRAM; + S32Megabytes max_vram(gGLManager.mVRAM); + + if(gGLManager.mIsATI) + { + //shrink the availabe vram for ATI cards because some of them do not handel texture swapping well. + max_vram = max_vram * 0.75f; + } + max_vram = llmax(max_vram, getMinVideoRamSetting()); max_texmem = max_vram; if (!get_recommended) @@ -1080,80 +1302,89 @@ S32 LLViewerTextureList::getMaxVideoRamSetting(bool get_recommended) } else { - if (get_recommended) - max_texmem = 128; + if (!get_recommended) + { + max_texmem = (S32Megabytes)512; + } + else if (gSavedSettings.getBOOL("NoHardwareProbe")) //did not do hardware detection at startup + { + max_texmem = (S32Megabytes)512; + } else - max_texmem = 512; - llwarns << "VRAM amount not detected, defaulting to " << max_texmem << " MB" << llendl; + { + max_texmem = (S32Megabytes)128; + } + + LL_WARNS() << "VRAM amount not detected, defaulting to " << max_texmem << " MB" << LL_ENDL; } - S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB - //llinfos << "*** DETECTED " << system_ram << " MB of system memory." << llendl; + S32Megabytes system_ram = gSysMemory.getPhysicalMemoryClamped(); // In MB + //LL_INFOS() << "*** DETECTED " << system_ram << " MB of system memory." << LL_ENDL; if (get_recommended) - max_texmem = llmin(max_texmem, (S32)(system_ram/2)); + max_texmem = llmin(max_texmem, system_ram/2); else - max_texmem = llmin(max_texmem, (S32)(system_ram)); + max_texmem = llmin(max_texmem, system_ram); - max_texmem = llclamp(max_texmem, getMinVideoRamSetting(), MAX_VIDEO_RAM_IN_MEGA_BYTES); + // limit the texture memory to a multiple of the default if we've found some cards to behave poorly otherwise + max_texmem = llmin(max_texmem, (S32Megabytes) (mem_multiplier * max_texmem)); + + max_texmem = llclamp(max_texmem, getMinVideoRamSetting(), gMaxVideoRam); return max_texmem; } -const S32 VIDEO_CARD_FRAMEBUFFER_MEM = 12; // MB -const S32 MIN_MEM_FOR_NON_TEXTURE = 512 ; //MB -void LLViewerTextureList::updateMaxResidentTexMem(S32 mem) +const S32Megabytes VIDEO_CARD_FRAMEBUFFER_MEM(12); +const S32Megabytes MIN_MEM_FOR_NON_TEXTURE(512); +void LLViewerTextureList::updateMaxResidentTexMem(S32Megabytes mem) { // Initialize the image pipeline VRAM settings - S32 cur_mem = gSavedSettings.getS32("TextureMemory"); + S32Megabytes cur_mem(gSavedSettings.getS32("TextureMemory")); F32 mem_multiplier = gSavedSettings.getF32("RenderTextureMemoryMultiple"); - S32 default_mem = getMaxVideoRamSetting(true); // recommended default - if (mem == 0) + S32Megabytes default_mem = getMaxVideoRamSetting(true, mem_multiplier); // recommended default + if (mem == (S32Bytes)0) { - mem = cur_mem > 0 ? cur_mem : default_mem; + mem = cur_mem > (S32Bytes)0 ? cur_mem : default_mem; } - else if (mem < 0) + else if (mem < (S32Bytes)0) { mem = default_mem; } - // limit the texture memory to a multiple of the default if we've found some cards to behave poorly otherwise - mem = llmin(mem, (S32) (mem_multiplier * (F32) default_mem)); - - mem = llclamp(mem, getMinVideoRamSetting(), getMaxVideoRamSetting()); + mem = llclamp(mem, getMinVideoRamSetting(), getMaxVideoRamSetting(false, mem_multiplier)); if (mem != cur_mem) { - gSavedSettings.setS32("TextureMemory", mem); + gSavedSettings.setS32("TextureMemory", mem.value()); return; //listener will re-enter this function } // TODO: set available resident texture mem based on use by other subsystems // currently max(12MB, VRAM/4) assumed... - S32 vb_mem = mem; - S32 fb_mem = llmax(VIDEO_CARD_FRAMEBUFFER_MEM, vb_mem/4); + S32Megabytes vb_mem = mem; + S32Megabytes fb_mem = llmax(VIDEO_CARD_FRAMEBUFFER_MEM, vb_mem/4); mMaxResidentTexMemInMegaBytes = (vb_mem - fb_mem) ; //in MB mMaxTotalTextureMemInMegaBytes = mMaxResidentTexMemInMegaBytes * 2; - if (mMaxResidentTexMemInMegaBytes > 640) + if (mMaxResidentTexMemInMegaBytes > (S32Megabytes)640) { - mMaxTotalTextureMemInMegaBytes -= (mMaxResidentTexMemInMegaBytes >> 2); + mMaxTotalTextureMemInMegaBytes -= (mMaxResidentTexMemInMegaBytes / 4); } //system mem - S32 system_ram = (S32)BYTES_TO_MEGA_BYTES(gSysMemory.getPhysicalMemoryClamped()); // In MB + S32Megabytes system_ram = gSysMemory.getPhysicalMemoryClamped(); //minimum memory reserved for non-texture use. //if system_raw >= 1GB, reserve at least 512MB for non-texture use; //otherwise reserve half of the system_ram for non-texture use. - S32 min_non_texture_mem = llmin(system_ram / 2, MIN_MEM_FOR_NON_TEXTURE) ; + S32Megabytes min_non_texture_mem = llmin(system_ram / 2, MIN_MEM_FOR_NON_TEXTURE) ; if (mMaxTotalTextureMemInMegaBytes > system_ram - min_non_texture_mem) { mMaxTotalTextureMemInMegaBytes = system_ram - min_non_texture_mem ; } - llinfos << "Total Video Memory set to: " << vb_mem << " MB" << llendl; - llinfos << "Available Texture Memory set to: " << (vb_mem - fb_mem) << " MB" << llendl; + LL_INFOS() << "Total Video Memory set to: " << vb_mem << " MB" << LL_ENDL; + LL_INFOS() << "Available Texture Memory set to: " << (vb_mem - fb_mem) << " MB" << LL_ENDL; } /////////////////////////////////////////////////////////////////////////////// @@ -1161,9 +1392,9 @@ void LLViewerTextureList::updateMaxResidentTexMem(S32 mem) // static void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_data) { - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic", false) ; - LLFastTimer t(FTM_PROCESS_IMAGES); + LL_RECORD_BLOCK_TIME(FTM_PROCESS_IMAGES); // Receive image header, copy into image object and decompresses // if this is a one-packet image. @@ -1173,17 +1404,17 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); - U32 received_size ; + U32Bytes received_size ; if (msg->getReceiveCompressedSize()) { - received_size = msg->getReceiveCompressedSize() ; + received_size = (U32Bytes)msg->getReceiveCompressedSize() ; } else { - received_size = msg->getReceiveSize() ; + received_size = (U32Bytes)msg->getReceiveSize() ; } - gTextureList.sTextureBits += received_size * 8; - gTextureList.sTexturePackets++; + add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, received_size); + add(LLStatViewer::TEXTURE_PACKETS, 1); U8 codec; U16 packets; @@ -1202,8 +1433,8 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d { // msg->getSizeFast() is probably trying to tell us there // was an error. - llerrs << "image header chunk size was negative: " - << data_size << llendl; + LL_ERRS() << "image header chunk size was negative: " + << data_size << LL_ENDL; return; } @@ -1211,7 +1442,7 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d U8 *data = new U8[data_size]; msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if (!image) { delete [] data; @@ -1233,10 +1464,9 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d // static void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_data) { - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic", false) ; - LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); - LLFastTimer t(FTM_PROCESS_IMAGES); + LL_RECORD_BLOCK_TIME(FTM_PROCESS_IMAGES); // Receives image packet, copy into image object, // checks if all packets received, decompresses if so. @@ -1247,17 +1477,18 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d char ip_string[256]; u32_to_ip_string(msg->getSenderIP(),ip_string); - U32 received_size ; + U32Bytes received_size ; if (msg->getReceiveCompressedSize()) { - received_size = msg->getReceiveCompressedSize() ; + received_size = (U32Bytes)msg->getReceiveCompressedSize() ; } else { - received_size = msg->getReceiveSize() ; + received_size = (U32Bytes)msg->getReceiveSize() ; } - gTextureList.sTextureBits += received_size * 8; - gTextureList.sTexturePackets++; + + add(LLStatViewer::TEXTURE_NETWORK_DATA_RECEIVED, F64Bytes(received_size)); + add(LLStatViewer::TEXTURE_PACKETS, 1); //llprintline("Start decode, image header..."); msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id); @@ -1272,19 +1503,19 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d { // msg->getSizeFast() is probably trying to tell us there // was an error. - llerrs << "image data chunk size was negative: " - << data_size << llendl; + LL_ERRS() << "image data chunk size was negative: " + << data_size << LL_ENDL; return; } if (data_size > MTUBYTES) { - llerrs << "image data chunk too large: " << data_size << " bytes" << llendl; + LL_ERRS() << "image data chunk too large: " << data_size << " bytes" << LL_ENDL; return; } U8 *data = new U8[data_size]; msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size); - LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, TRUE, LLViewerTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + LLViewerFetchedTexture *image = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); if (!image) { delete [] data; @@ -1308,34 +1539,18 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d // static void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void **user_data) { - LLFastTimer t(FTM_PROCESS_IMAGES); + LL_RECORD_BLOCK_TIME(FTM_PROCESS_IMAGES); LLUUID image_id; msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, image_id); LLViewerFetchedTexture* image = gTextureList.findImage( image_id ); if( image ) { + LL_WARNS() << "not in db" << LL_ENDL; image->setIsMissingAsset(); } } -/////////////////////////////////////////////////////////////////////////////// - -//static -const U32 SIXTEEN_MEG = 0x1000000; -S32 LLViewerTextureList::calcMaxTextureRAM() -{ - // Decide the maximum amount of RAM we should allow the user to allocate to texture cache - LLMemoryInfo memory_info; - U32 available_memory = memory_info.getPhysicalMemoryClamped(); - - clamp_rescale((F32)available_memory, - (F32)(SIXTEEN_MEG * 16), - (F32)U32_MAX, - (F32)(SIXTEEN_MEG * 4), - (F32)(U32_MAX >> 1)); - return available_memory; -} /////////////////////////////////////////////////////////////////////////////// @@ -1362,7 +1577,8 @@ LLUIImagePtr LLUIImageList::getUIImageByID(const LLUUID& image_id, S32 priority) const BOOL use_mips = FALSE; const LLRect scale_rect = LLRect::null; - return loadUIImageByID(image_id, use_mips, scale_rect, (LLViewerTexture::EBoostLevel)priority); + const LLRect clip_rect = LLRect::null; + return loadUIImageByID(image_id, use_mips, scale_rect, clip_rect, (LLViewerTexture::EBoostLevel)priority); } LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priority) @@ -1376,69 +1592,79 @@ LLUIImagePtr LLUIImageList::getUIImage(const std::string& image_name, S32 priori const BOOL use_mips = FALSE; const LLRect scale_rect = LLRect::null; - return loadUIImageByName(image_name, image_name, use_mips, scale_rect, (LLViewerTexture::EBoostLevel)priority); + const LLRect clip_rect = LLRect::null; + return loadUIImageByName(image_name, image_name, use_mips, scale_rect, clip_rect, (LLViewerTexture::EBoostLevel)priority); } LLUIImagePtr LLUIImageList::loadUIImageByName(const std::string& name, const std::string& filename, - BOOL use_mips, const LLRect& scale_rect, LLViewerTexture::EBoostLevel boost_priority ) + BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority, + LLUIImage::EScaleStyle scale_style) { - if (boost_priority == LLViewerTexture::BOOST_NONE) + if (boost_priority == LLGLTexture::BOOST_NONE) { - boost_priority = LLViewerTexture::BOOST_UI; + boost_priority = LLGLTexture::BOOST_UI; } - LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTextureFromFile(filename, MIPMAP_NO, boost_priority); - return loadUIImage(imagep, name, use_mips, scale_rect); + LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTextureFromFile(filename, FTT_LOCAL_FILE, MIPMAP_NO, boost_priority); + return loadUIImage(imagep, name, use_mips, scale_rect, clip_rect, scale_style); } LLUIImagePtr LLUIImageList::loadUIImageByID(const LLUUID& id, - BOOL use_mips, const LLRect& scale_rect, LLViewerTexture::EBoostLevel boost_priority) + BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLViewerTexture::EBoostLevel boost_priority, + LLUIImage::EScaleStyle scale_style) { - if (boost_priority == LLViewerTexture::BOOST_NONE) + if (boost_priority == LLGLTexture::BOOST_NONE) { - boost_priority = LLViewerTexture::BOOST_UI; + boost_priority = LLGLTexture::BOOST_UI; } - LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(id, MIPMAP_NO, boost_priority); - return loadUIImage(imagep, id.asString(), use_mips, scale_rect); + LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(id, FTT_DEFAULT, MIPMAP_NO, boost_priority); + return loadUIImage(imagep, id.asString(), use_mips, scale_rect, clip_rect, scale_style); } -LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const std::string& name, BOOL use_mips, const LLRect& scale_rect) +LLUIImagePtr LLUIImageList::loadUIImage(LLViewerFetchedTexture* imagep, const std::string& name, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, + LLUIImage::EScaleStyle scale_style) { if (!imagep) return NULL; imagep->setAddressMode(LLTexUnit::TAM_CLAMP); + //don't compress UI images + imagep->getGLTexture()->setAllowCompression(false); + //all UI images are non-deletable imagep->setNoDelete(); LLUIImagePtr new_imagep = new LLUIImage(name, imagep); + new_imagep->setScaleStyle(scale_style); + mUIImages.insert(std::make_pair(name, new_imagep)); mUITextureList.push_back(imagep); //Note: //Some other textures such as ICON also through this flow to be fetched. //But only UI textures need to set this callback. - if(imagep->getBoostLevel() == LLViewerTexture::BOOST_UI) + if(imagep->getBoostLevel() == LLGLTexture::BOOST_UI) { LLUIImageLoadData* datap = new LLUIImageLoadData; datap->mImageName = name; datap->mImageScaleRegion = scale_rect; + datap->mImageClipRegion = clip_rect; imagep->setLoadedCallback(onUIImageLoaded, 0, FALSE, FALSE, datap, NULL); } return new_imagep; } -LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::string& filename, BOOL use_mips, const LLRect& scale_rect) +LLUIImagePtr LLUIImageList::preloadUIImage(const std::string& name, const std::string& filename, BOOL use_mips, const LLRect& scale_rect, const LLRect& clip_rect, LLUIImage::EScaleStyle scale_style) { // look for existing image uuid_ui_image_map_t::iterator found_it = mUIImages.find(name); if (found_it != mUIImages.end()) { // image already loaded! - llerrs << "UI Image " << name << " already loaded." << llendl; + LL_ERRS() << "UI Image " << name << " already loaded." << LL_ENDL; } - return loadUIImageByName(name, filename, use_mips, scale_rect); + return loadUIImageByName(name, filename, use_mips, scale_rect, clip_rect, LLGLTexture::BOOST_UI, scale_style); } //static @@ -1452,6 +1678,7 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_v LLUIImageLoadData* image_datap = (LLUIImageLoadData*)user_data; std::string ui_image_name = image_datap->mImageName; LLRect scale_rect = image_datap->mImageScaleRegion; + LLRect clip_rect = image_datap->mImageClipRegion; if (final) { delete image_datap; @@ -1468,9 +1695,21 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_v // from power-of-2 gl image if (success && imagep.notNull() && src_vi && (src_vi->getUrl().compare(0, 7, "file://")==0)) { - F32 clip_x = (F32)src_vi->getOriginalWidth() / (F32)src_vi->getFullWidth(); - F32 clip_y = (F32)src_vi->getOriginalHeight() / (F32)src_vi->getFullHeight(); - imagep->setClipRegion(LLRectf(0.f, clip_y, clip_x, 0.f)); + F32 full_width = (F32)src_vi->getFullWidth(); + F32 full_height = (F32)src_vi->getFullHeight(); + F32 clip_x = (F32)src_vi->getOriginalWidth() / full_width; + F32 clip_y = (F32)src_vi->getOriginalHeight() / full_height; + if (clip_rect != LLRect::null) + { + imagep->setClipRegion(LLRectf(llclamp((F32)clip_rect.mLeft / full_width, 0.f, 1.f), + llclamp((F32)clip_rect.mTop / full_height, 0.f, 1.f), + llclamp((F32)clip_rect.mRight / full_width, 0.f, 1.f), + llclamp((F32)clip_rect.mBottom / full_height, 0.f, 1.f))); + } + else + { + imagep->setClipRegion(LLRectf(0.f, clip_y, clip_x, 0.f)); + } if (scale_rect != LLRect::null) { imagep->setScaleRegion( @@ -1485,20 +1724,37 @@ void LLUIImageList::onUIImageLoaded( BOOL success, LLViewerFetchedTexture *src_v } } +namespace LLInitParam +{ + template<> + struct TypeValues<LLUIImage::EScaleStyle> : public TypeValuesHelper<LLUIImage::EScaleStyle> + { + static void declareValues() + { + declare("scale_inner", LLUIImage::SCALE_INNER); + declare("scale_outer", LLUIImage::SCALE_OUTER); + } + }; +} + struct UIImageDeclaration : public LLInitParam::Block<UIImageDeclaration> { - Mandatory<std::string> name; - Optional<std::string> file_name; - Optional<bool> preload; - Optional<LLRect> scale; - Optional<bool> use_mips; + Mandatory<std::string> name; + Optional<std::string> file_name; + Optional<bool> preload; + Optional<LLRect> scale; + Optional<LLRect> clip; + Optional<bool> use_mips; + Optional<LLUIImage::EScaleStyle> scale_type; UIImageDeclaration() : name("name"), file_name("file_name"), preload("preload", false), scale("scale"), - use_mips("use_mips", false) + clip("clip"), + use_mips("use_mips", false), + scale_type("scale_type", LLUIImage::SCALE_INNER) {} }; @@ -1515,49 +1771,43 @@ struct UIImageDeclarations : public LLInitParam::Block<UIImageDeclarations> bool LLUIImageList::initFromFile() { - // construct path to canonical textures.xml in default skin dir - std::string base_file_path = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "default", "textures", "textures.xml"); + // Look for textures.xml in all the right places. Pass + // constraint=LLDir::ALL_SKINS because we want to overlay textures.xml + // from all the skins directories. + std::vector<std::string> textures_paths = + gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", LLDir::ALL_SKINS); + std::vector<std::string>::const_iterator pi(textures_paths.begin()), pend(textures_paths.end()); + if (pi == pend) + { + LL_WARNS() << "No textures.xml found in skins directories" << LL_ENDL; + return false; + } + // The first (most generic) file gets special validations LLXMLNodePtr root; - - if (!LLXMLNode::parseFile(base_file_path, root, NULL)) + if (!LLXMLNode::parseFile(*pi, root, NULL)) { - llwarns << "Unable to parse UI image list file " << base_file_path << llendl; + LL_WARNS() << "Unable to parse UI image list file " << *pi << LL_ENDL; return false; } if (!root->hasAttribute("version")) { - llwarns << "No valid version number in UI image list file " << base_file_path << llendl; + LL_WARNS() << "No valid version number in UI image list file " << *pi << LL_ENDL; return false; } UIImageDeclarations images; LLXUIParser parser; - parser.readXUI(root, images, base_file_path); - - // add components defined in current skin - std::string skin_update_path = gDirUtilp->getSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - LLXMLNodePtr update_root; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) - { - parser.readXUI(update_root, images, skin_update_path); - } + parser.readXUI(root, images, *pi); - // add components defined in user override of current skin - skin_update_path = gDirUtilp->getUserSkinDir() - + gDirUtilp->getDirDelimiter() - + "textures" - + gDirUtilp->getDirDelimiter() - + "textures.xml"; - if (skin_update_path != base_file_path - && LLXMLNode::parseFile(skin_update_path, update_root, NULL)) + // add components defined in the rest of the skin paths + while (++pi != pend) { - parser.readXUI(update_root, images, skin_update_path); + LLXMLNodePtr update_root; + if (LLXMLNode::parseFile(*pi, update_root, NULL)) + { + parser.readXUI(update_root, images, *pi); + } } if (!images.validateBlock()) return false; @@ -1592,7 +1842,7 @@ bool LLUIImageList::initFromFile() { continue; } - preloadUIImage(image.name, file_name, image.use_mips, image.scale); + preloadUIImage(image.name, file_name, image.use_mips, image.scale, image.clip, image.scale_type); } if (cur_pass == PASS_DECODE_NOW && !gSavedSettings.getBOOL("NoPreload")) |