Files
SingularityViewer/indra/newview/llviewertexture.cpp
Aleric Inglewood 1bc876542c Avoid possible crash on opensim.
See http://redmine.kokuaviewer.org/issues/582

This happened only on opensim. The fixed was made
basically by MichelleZ. It doesn't harm anything, so
even though it appears to never happen on Agni
and it is unknown what bug is causing it, it make the
viewer resilient just-in-case...
2011-05-14 19:38:03 +02:00

4048 lines
110 KiB
C++

/**
* @file llviewertexture.cpp
* @brief Object which handles a received image (and associated texture(s))
*
* $LicenseInfo:firstyear=2000&license=viewergpl$
*
* Copyright (c) 2000-2010, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlife.com/developers/opensource/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlife.com/developers/opensource/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*
*/
#include "llviewerprecompiledheaders.h"
#include "llviewertexture.h"
// Library includes
#include "imageids.h"
#include "llmath.h"
#include "llerror.h"
#include "llgl.h"
#include "llglheaders.h"
#include "llhost.h"
#include "llimage.h"
#include "llimagebmp.h"
#include "llimagej2c.h"
#include "llimagetga.h"
#include "llmemtype.h"
#include "llstl.h"
#include "llvfile.h"
#include "llvfs.h"
#include "message.h"
#include "lltimer.h"
// viewer includes
#include "llimagegl.h"
#include "lldrawpool.h"
#include "lltexturefetch.h"
#include "llviewertexturelist.h"
#include "llviewercontrol.h"
#include "pipeline.h"
#include "llappviewer.h"
#include "llface.h"
#include "llviewercamera.h"
//#include "lltextureatlas.h"
//#include "lltextureatlasmanager.h"
#include "lltextureentry.h"
//#include "llmediaentry.h"
#include "llvovolume.h"
#include "llviewermedia.h"
///////////////////////////////////////////////////////////////////////////////
// statics
LLPointer<LLViewerTexture> LLViewerTexture::sNullImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL;
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sSmokeImagep = NULL;
#if NEW_MEDIA_TEXTURE
LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap ;
#endif
//LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL ;
//const std::string sTesterName("TextureTester");
S32 LLViewerTexture::sImageCount = 0;
S32 LLViewerTexture::sRawCount = 0;
S32 LLViewerTexture::sAuxCount = 0;
LLFrameTimer LLViewerTexture::sEvaluationTimer;
F32 LLViewerTexture::sDesiredDiscardBias = 0.f;
F32 LLViewerTexture::sDesiredDiscardScale = 1.1f;
S32 LLViewerTexture::sBoundTextureMemoryInBytes = 0;
S32 LLViewerTexture::sTotalTextureMemoryInBytes = 0;
S32 LLViewerTexture::sMaxBoundTextureMemInMegaBytes = 0;
S32 LLViewerTexture::sMaxTotalTextureMemInMegaBytes = 0;
S32 LLViewerTexture::sMaxDesiredTextureMemInBytes = 0 ;
S8 LLViewerTexture::sCameraMovingDiscardBias = 0 ;
S32 LLViewerTexture::sMaxSculptRez = 128 ; //max sculpt image size
const S32 MAX_CACHED_RAW_IMAGE_AREA = 64 * 64 ;
const S32 MAX_CACHED_RAW_SCULPT_IMAGE_AREA = LLViewerTexture::sMaxSculptRez * LLViewerTexture::sMaxSculptRez ;
const S32 MAX_CACHED_RAW_TERRAIN_IMAGE_AREA = 128 * 128 ;
S32 LLViewerTexture::sMinLargeImageSize = 65536 ; //256 * 256.
S32 LLViewerTexture::sMaxSmallImageSize = MAX_CACHED_RAW_IMAGE_AREA ;
BOOL LLViewerTexture::sFreezeImageScalingDown = FALSE ;
F32 LLViewerTexture::sCurrentTime = 0.0f ;
//BOOL LLViewerTexture::sUseTextureAtlas = FALSE ;
BOOL LLViewerTexture::sDontLoadVolumeTextures = FALSE ;
const F32 desired_discard_bias_min = -2.0f; // -max number of levels to improve image quality by
const F32 desired_discard_bias_max = (F32)MAX_DISCARD_LEVEL; // max number of levels to reduce image quality by
const F64 log_2 = log(2.0);
//----------------------------------------------------------------------------------------------
//namespace: LLViewerTextureAccess
//----------------------------------------------------------------------------------------------
LLLoadedCallbackEntry::LLLoadedCallbackEntry(loaded_callback_func cb,
S32 discard_level,
BOOL need_imageraw, // Needs image raw for the callback
void* userdata,
LLLoadedCallbackEntry::source_callback_list_t* src_callback_list,
LLViewerFetchedTexture* target,
BOOL pause)
: mCallback(cb),
mLastUsedDiscard(MAX_DISCARD_LEVEL+1),
mDesiredDiscard(discard_level),
mNeedsImageRaw(need_imageraw),
mUserData(userdata),
mSourceCallbackList(src_callback_list),
mPaused(pause)
{
if(mSourceCallbackList)
{
mSourceCallbackList->insert(target->getID());
}
}
LLLoadedCallbackEntry::~LLLoadedCallbackEntry()
{
}
void LLLoadedCallbackEntry::removeTexture(LLViewerFetchedTexture* tex)
{
if(mSourceCallbackList)
{
mSourceCallbackList->erase(tex->getID()) ;
}
}
//static
void LLLoadedCallbackEntry::cleanUpCallbackList(LLLoadedCallbackEntry::source_callback_list_t* callback_list)
{
//clear texture callbacks.
if(callback_list && !callback_list->empty())
{
for(LLLoadedCallbackEntry::source_callback_list_t::iterator iter = callback_list->begin();
iter != callback_list->end(); ++iter)
{
LLViewerFetchedTexture* tex = gTextureList.findImage(*iter) ;
if(tex)
{
tex->deleteCallbackEntry(callback_list) ;
}
}
callback_list->clear() ;
}
}
#if NEW_MEDIA_TEXTURE
LLViewerMediaTexture* LLViewerTextureManager::createMediaTexture(const LLUUID &media_id, BOOL usemipmaps, LLImageGL* gl_image)
{
return new LLViewerMediaTexture(media_id, usemipmaps, gl_image) ;
}
#endif
LLViewerTexture* LLViewerTextureManager::findTexture(const LLUUID& id)
{
LLViewerTexture* tex ;
//search fetched texture list
tex = gTextureList.findImage(id) ;
#if NEW_MEDIA_TEXTURE
//search media texture list
if(!tex)
{
tex = LLViewerTextureManager::findMediaTexture(id) ;
}
#endif
return tex ;
}
#if NEW_MEDIA_TEXTURE
LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id)
{
return LLViewerMediaTexture::findMediaTexture(media_id) ;
}
LLViewerMediaTexture* LLViewerTextureManager::getMediaTexture(const LLUUID& id, BOOL usemipmaps, LLImageGL* gl_image)
{
LLViewerMediaTexture* tex = LLViewerMediaTexture::findMediaTexture(id) ;
if(!tex)
{
tex = LLViewerTextureManager::createMediaTexture(id, usemipmaps, gl_image) ;
}
tex->initVirtualSize() ;
return tex ;
}
#endif
LLViewerFetchedTexture* LLViewerTextureManager::staticCastToFetchedTexture(LLTexture* tex, BOOL report_error)
{
if(!tex)
{
return NULL ;
}
S8 type = tex->getType() ;
if(type == LLViewerTexture::FETCHED_TEXTURE || type == LLViewerTexture::LOD_TEXTURE)
{
return static_cast<LLViewerFetchedTexture*>(tex) ;
}
if(report_error)
{
llerrs << "not a fetched texture type: " << type << llendl ;
}
return NULL ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(usemipmaps) ;
if(generate_gl_tex)
{
tex->generateGLTexture() ;
tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLUUID& id, BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(id, usemipmaps) ;
if(generate_gl_tex)
{
tex->generateGLTexture() ;
tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps) ;
tex->setCategory(LLViewerTexture::LOCAL) ;
return tex ;
}
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex)
{
LLPointer<LLViewerTexture> tex = new LLViewerTexture(width, height, components, usemipmaps) ;
if(generate_gl_tex)
{
tex->generateGLTexture() ;
tex->setCategory(LLViewerTexture::LOCAL) ;
}
return tex ;
}
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture(
const LLUUID &image_id,
BOOL usemipmaps,
LLViewerTexture::EBoostLevel boost_priority,
S8 texture_type,
LLGLint internal_format,
LLGLenum primary_format,
LLHost request_from_host)
{
return gTextureList.getImage(image_id, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host) ;
}
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile(
const std::string& filename,
BOOL usemipmaps,
LLViewerTexture::EBoostLevel boost_priority,
S8 texture_type,
LLGLint internal_format,
LLGLenum primary_format,
const LLUUID& force_id)
{
return gTextureList.getImageFromFile(filename, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ;
}
//static
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string& url,
BOOL usemipmaps,
LLViewerTexture::EBoostLevel boost_priority,
S8 texture_type,
LLGLint internal_format,
LLGLenum primary_format,
const LLUUID& force_id
)
{
return gTextureList.getImageFromUrl(url, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id) ;
}
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, LLHost host)
{
return gTextureList.getImageFromHost(image_id, host) ;
}
void LLViewerTextureManager::init()
{
LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,3);
raw->clear(0x77, 0x77, 0x77, 0xFF);
LLViewerTexture::sNullImagep = LLViewerTextureManager::getLocalTexture(raw.get(), TRUE) ;
#if 1
LLPointer<LLViewerFetchedTexture> imagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT);
LLViewerFetchedTexture::sDefaultImagep = imagep;
const S32 dim = 128;
LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
U8* data = image_raw->getData();
for (S32 i = 0; i<dim; i++)
{
for (S32 j = 0; j<dim; j++)
{
#if 0
const S32 border = 2;
if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
{
*data++ = 0xff;
*data++ = 0xff;
*data++ = 0xff;
}
else
#endif
{
*data++ = 0x7f;
*data++ = 0x7f;
*data++ = 0x7f;
}
}
}
imagep->createGLTexture(0, image_raw);
//cache the raw image
imagep->setCachedRawImage(0, image_raw) ;
image_raw = NULL;
#else
LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, LLViewerTexture::BOOST_UI);
#endif
LLViewerFetchedTexture::sDefaultImagep->dontDiscard();
LLViewerFetchedTexture::sDefaultImagep->setCategory(LLViewerTexture::OTHER) ;
LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, LLViewerTexture::BOOST_UI);
LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ;
LLViewerTexture::initClass() ;
/*if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
{
sTesterp = new LLTexturePipelineTester() ;
if (!sTesterp->isValid())
{
delete sTesterp;
sTesterp = NULL;
}
}*/
}
void LLViewerTextureManager::cleanup()
{
stop_glerror();
LLImageGL::sDefaultGLTexture = NULL ;
LLViewerTexture::sNullImagep = NULL;
LLViewerFetchedTexture::sDefaultImagep = NULL;
LLViewerFetchedTexture::sSmokeImagep = NULL;
LLViewerFetchedTexture::sMissingAssetImagep = NULL;
LLViewerFetchedTexture::sWhiteImagep = NULL;
#if NEW_MEDIA_TEXTURE
LLViewerMediaTexture::cleanUpClass() ;
#endif
}
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//start of LLViewerTexture
//----------------------------------------------------------------------------------------------
// static
void LLViewerTexture::initClass()
{
LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ;
if(gAuditTexture)
{
LLImageGL::setHighlightTexture(LLViewerTexture::OTHER) ;
}
}
// static
S32 LLViewerTexture::getTotalNumOfCategories()
{
return MAX_GL_IMAGE_CATEGORY - (BOOST_HIGH - BOOST_SCULPTED) + 2 ;
}
// static
//index starts from zero.
S32 LLViewerTexture::getIndexFromCategory(S32 category)
{
return (category < BOOST_HIGH) ? category : category - (BOOST_HIGH - BOOST_SCULPTED) + 1 ;
}
//static
S32 LLViewerTexture::getCategoryFromIndex(S32 index)
{
return (index < BOOST_HIGH) ? index : index + (BOOST_HIGH - BOOST_SCULPTED) - 1 ;
}
// tuning params
const F32 discard_bias_delta = .25f;
const F32 discard_delta_time = 0.5f;
const S32 min_non_tex_system_mem = (128<<20); // 128 MB
// non-const (used externally
F32 texmem_lower_bound_scale = 0.85f;
F32 texmem_middle_bound_scale = 0.925f;
//static
void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity)
{
sCurrentTime = gFrameTimeSeconds ;
/*LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
tester->update() ;
}*/
#if NEW_MEDIA_TEXTURE
LLViewerMediaTexture::updateClass() ;
#endif
sBoundTextureMemoryInBytes = LLImageGL::sBoundTextureMemoryInBytes;//in bytes
sTotalTextureMemoryInBytes = LLImageGL::sGlobalTextureMemoryInBytes;//in bytes
sMaxBoundTextureMemInMegaBytes = gTextureList.getMaxResidentTexMem();//in MB
sMaxTotalTextureMemInMegaBytes = gTextureList.getMaxTotalTextureMem() ;//in MB
sMaxDesiredTextureMemInBytes = MEGA_BYTES_TO_BYTES(sMaxTotalTextureMemInMegaBytes) ; //in Bytes, by default and when total used texture memory is small.
if (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) >= sMaxBoundTextureMemInMegaBytes ||
BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) >= sMaxTotalTextureMemInMegaBytes)
{
//when texture memory overflows, lower down the threashold to release the textures more aggressively.
sMaxDesiredTextureMemInBytes = llmin((S32)(sMaxDesiredTextureMemInBytes * 0.75f) , MEGA_BYTES_TO_BYTES(MAX_VIDEO_RAM_IN_MEGA_BYTES)) ;//512 MB
// If we are using more texture memory than we should,
// scale up the desired discard level
if (sEvaluationTimer.getElapsedTimeF32() > discard_delta_time)
{
sDesiredDiscardBias += discard_bias_delta;
sEvaluationTimer.reset();
}
}
else if (sDesiredDiscardBias > 0.0f &&
BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < sMaxBoundTextureMemInMegaBytes * texmem_lower_bound_scale &&
BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < sMaxTotalTextureMemInMegaBytes * texmem_lower_bound_scale)
{
// If we are using less texture memory than we should,
// scale down the desired discard level
if (sEvaluationTimer.getElapsedTimeF32() > discard_delta_time)
{
sDesiredDiscardBias -= discard_bias_delta;
sEvaluationTimer.reset();
}
}
sDesiredDiscardBias = llclamp(sDesiredDiscardBias, desired_discard_bias_min, desired_discard_bias_max);
//LLViewerTexture::sUseTextureAtlas = gSavedSettings.getBOOL("EnableTextureAtlas") ;
F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed() ;
F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
sCameraMovingDiscardBias = (S8)llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1) ;
LLViewerTexture::sFreezeImageScalingDown = (BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) < 0.75f * sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) &&
(BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) < 0.75f * sMaxTotalTextureMemInMegaBytes * texmem_middle_bound_scale) ;
}
//end of static functions
//-------------------------------------------------------------------------------------------
const U32 LLViewerTexture::sCurrentFileVersion = 1;
LLViewerTexture::LLViewerTexture(BOOL usemipmaps)
{
init(true);
mUseMipMaps = usemipmaps ;
mID.generate();
sImageCount++;
}
LLViewerTexture::LLViewerTexture(const LLUUID& id, BOOL usemipmaps)
: mID(id)
{
init(true);
mUseMipMaps = usemipmaps ;
sImageCount++;
}
LLViewerTexture::LLViewerTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps)
{
init(true);
mFullWidth = width ;
mFullHeight = height ;
mUseMipMaps = usemipmaps ;
mComponents = components ;
setTexelsPerImage();
mID.generate();
sImageCount++;
}
LLViewerTexture::LLViewerTexture(const LLImageRaw* raw, BOOL usemipmaps)
{
init(true);
mUseMipMaps = usemipmaps ;
mGLTexturep = new LLImageGL(raw, usemipmaps) ;
// Create an empty image of the specified size and width
mID.generate();
sImageCount++;
}
LLViewerTexture::~LLViewerTexture()
{
cleanup();
sImageCount--;
}
void LLViewerTexture::init(bool firstinit)
{
mBoostLevel = LLViewerTexture::BOOST_NONE;
mFullWidth = 0;
mFullHeight = 0;
mTexelsPerImage = 0 ;
mUseMipMaps = FALSE ;
mComponents = 0 ;
mTextureState = NO_DELETE ;
mDontDiscard = FALSE;
mMaxVirtualSize = 0.f;
mNeedsGLTexture = FALSE ;
mMaxVirtualSizeResetInterval = 1;
mMaxVirtualSizeResetCounter = mMaxVirtualSizeResetInterval ;
mAdditionalDecodePriority = 0.f ;
mParcelMedia = NULL ;
mNumFaces = 0 ;
mNumVolumes = 0;
mFaceList.clear() ;
mVolumeList.clear();
mIsMediaTexture = false;
}
//virtual
S8 LLViewerTexture::getType() const
{
return LLViewerTexture::LOCAL_TEXTURE ;
}
void LLViewerTexture::cleanup()
{
mFaceList.clear() ;
mVolumeList.clear();
if(mGLTexturep)
{
mGLTexturep->cleanup();
}
}
// virtual
void LLViewerTexture::dump()
{
if(mGLTexturep)
{
mGLTexturep->dump();
}
llinfos << "LLViewerTexture"
<< " mID " << mID
<< llendl;
}
void LLViewerTexture::setBoostLevel(S32 level)
{
if(mBoostLevel != level)
{
mBoostLevel = level ;
if(mBoostLevel != LLViewerTexture::BOOST_NONE)
{
setNoDelete() ;
}
if(gAuditTexture)
{
setCategory(mBoostLevel);
}
}
}
bool LLViewerTexture::bindDefaultImage(S32 stage)
{
if (stage < 0) return false;
bool res = true;
if (LLViewerFetchedTexture::sDefaultImagep.notNull() && (this != LLViewerFetchedTexture::sDefaultImagep.get()))
{
// use default if we've got it
res = gGL.getTexUnit(stage)->bind(LLViewerFetchedTexture::sDefaultImagep);
}
if (!res && LLViewerTexture::sNullImagep.notNull() && (this != LLViewerTexture::sNullImagep))
{
res = gGL.getTexUnit(stage)->bind(LLViewerTexture::sNullImagep) ;
}
if (!res)
{
llwarns << "LLViewerTexture::bindDefaultImage failed." << llendl;
}
stop_glerror();
//check if there is cached raw image and switch to it if possible
switchToCachedImage() ;
/*LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
tester->updateGrayTextureBinding() ;
}*/
return res;
}
//virtual
BOOL LLViewerTexture::isMissingAsset()const
{
return FALSE;
}
//virtual
void LLViewerTexture::forceImmediateUpdate()
{
}
void LLViewerTexture::addTextureStats(F32 virtual_size, BOOL needs_gltexture) const
{
if(needs_gltexture)
{
mNeedsGLTexture = TRUE ;
}
if(!mMaxVirtualSizeResetCounter)
{
//flag to reset the values because the old values are used.
resetMaxVirtualSizeResetCounter() ;
mMaxVirtualSize = virtual_size;
mAdditionalDecodePriority = 0.f ;
mNeedsGLTexture = needs_gltexture ;
}
else if (virtual_size > mMaxVirtualSize)
{
mMaxVirtualSize = virtual_size;
}
}
void LLViewerTexture::resetTextureStats()
{
mMaxVirtualSize = 0.0f ;
mAdditionalDecodePriority = 0.f ;
mMaxVirtualSizeResetCounter = 0 ;
}
//virtual
F32 LLViewerTexture::getMaxVirtualSize()
{
return mMaxVirtualSize ;
}
//virtual
void LLViewerTexture::setKnownDrawSize(S32 width, S32 height)
{
//nothing here.
}
//virtual
void LLViewerTexture::addFace(LLFace* facep)
{
if(mNumFaces >= mFaceList.size())
{
mFaceList.resize(2 * mNumFaces + 1) ;
}
mFaceList[mNumFaces] = facep ;
facep->setIndexInTex(mNumFaces) ;
mNumFaces++ ;
mLastFaceListUpdateTimer.reset() ;
}
//virtual
void LLViewerTexture::removeFace(LLFace* facep)
{
if(mNumFaces > 1)
{
S32 index = facep->getIndexInTex() ;
mFaceList[index] = mFaceList[--mNumFaces] ;
mFaceList[index]->setIndexInTex(index) ;
}
else
{
mFaceList.clear() ;
mNumFaces = 0 ;
}
mLastFaceListUpdateTimer.reset() ;
}
S32 LLViewerTexture::getNumFaces() const
{
return mNumFaces ;
}
//virtual
void LLViewerTexture::addVolume(LLVOVolume* volumep)
{
if( mNumVolumes >= mVolumeList.size())
{
mVolumeList.resize(2 * mNumVolumes + 1) ;
}
mVolumeList[mNumVolumes] = volumep ;
volumep->setIndexInTex(mNumVolumes) ;
mNumVolumes++ ;
mLastVolumeListUpdateTimer.reset() ;
}
//virtual
void LLViewerTexture::removeVolume(LLVOVolume* volumep)
{
if(mNumVolumes > 1)
{
S32 index = volumep->getIndexInTex() ;
mVolumeList[index] = mVolumeList[--mNumVolumes] ;
mVolumeList[index]->setIndexInTex(index) ;
}
else
{
mVolumeList.clear() ;
mNumVolumes = 0 ;
}
mLastVolumeListUpdateTimer.reset() ;
}
S32 LLViewerTexture::getNumVolumes() const
{
return mNumVolumes ;
}
void LLViewerTexture::reorganizeFaceList()
{
static const F32 MAX_WAIT_TIME = 20.f; // seconds
static const U32 MAX_EXTRA_BUFFER_SIZE = 4 ;
if(mNumFaces + MAX_EXTRA_BUFFER_SIZE > mFaceList.size())
{
return ;
}
if(mLastFaceListUpdateTimer.getElapsedTimeF32() < MAX_WAIT_TIME)
{
return ;
}
mLastFaceListUpdateTimer.reset() ;
mFaceList.erase(mFaceList.begin() + mNumFaces, mFaceList.end());
}
void LLViewerTexture::reorganizeVolumeList()
{
static const F32 MAX_WAIT_TIME = 20.f; // seconds
static const U32 MAX_EXTRA_BUFFER_SIZE = 4 ;
if(mNumVolumes + MAX_EXTRA_BUFFER_SIZE > mVolumeList.size())
{
return ;
}
if(mLastVolumeListUpdateTimer.getElapsedTimeF32() < MAX_WAIT_TIME)
{
return ;
}
mLastVolumeListUpdateTimer.reset() ;
mVolumeList.erase(mVolumeList.begin() + mNumVolumes, mVolumeList.end());
}
//virtual
void LLViewerTexture::switchToCachedImage()
{
//nothing here.
}
void LLViewerTexture::forceActive()
{
mTextureState = ACTIVE ;
}
void LLViewerTexture::setActive()
{
if(mTextureState != NO_DELETE)
{
mTextureState = ACTIVE ;
}
}
//set the texture to stay in memory
void LLViewerTexture::setNoDelete()
{
mTextureState = NO_DELETE ;
}
void LLViewerTexture::generateGLTexture()
{
if(mGLTexturep.isNull())
{
mGLTexturep = new LLImageGL(mFullWidth, mFullHeight, mComponents, mUseMipMaps) ;
}
}
LLImageGL* LLViewerTexture::getGLTexture() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep ;
}
BOOL LLViewerTexture::createGLTexture()
{
if(mGLTexturep.isNull())
{
generateGLTexture() ;
}
return mGLTexturep->createGLTexture() ;
}
BOOL LLViewerTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename, BOOL to_create, S32 category)
{
llassert(mGLTexturep.notNull()) ;
BOOL ret = mGLTexturep->createGLTexture(discard_level, imageraw, usename, to_create, category) ;
if(ret)
{
mFullWidth = mGLTexturep->getCurrentWidth() ;
mFullHeight = mGLTexturep->getCurrentHeight() ;
mComponents = mGLTexturep->getComponents() ;
setTexelsPerImage();
}
return ret ;
}
//virtual
void LLViewerTexture::setCachedRawImage(S32 discard_level, LLImageRaw* imageraw)
{
//nothing here.
}
void LLViewerTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes)
{
llassert(mGLTexturep.notNull()) ;
mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes) ;
}
void LLViewerTexture::setAddressMode(LLTexUnit::eTextureAddressMode mode)
{
llassert(mGLTexturep.notNull()) ;
mGLTexturep->setAddressMode(mode) ;
}
void LLViewerTexture::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
{
llassert(mGLTexturep.notNull()) ;
mGLTexturep->setFilteringOption(option) ;
}
//virtual
S32 LLViewerTexture::getWidth(S32 discard_level) const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getWidth(discard_level) ;
}
//virtual
S32 LLViewerTexture::getHeight(S32 discard_level) const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getHeight(discard_level) ;
}
S32 LLViewerTexture::getMaxDiscardLevel() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getMaxDiscardLevel() ;
}
S32 LLViewerTexture::getDiscardLevel() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getDiscardLevel() ;
}
S8 LLViewerTexture::getComponents() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getComponents() ;
}
LLGLuint LLViewerTexture::getTexName() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getTexName() ;
}
BOOL LLViewerTexture::hasGLTexture() const
{
if(mGLTexturep.notNull())
{
return mGLTexturep->getHasGLTexture() ;
}
return FALSE ;
}
BOOL LLViewerTexture::getBoundRecently() const
{
if(mGLTexturep.notNull())
{
return mGLTexturep->getBoundRecently() ;
}
return FALSE ;
}
LLTexUnit::eTextureType LLViewerTexture::getTarget(void) const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getTarget() ;
}
BOOL LLViewerTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, bool fast_update)
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height, fast_update) ;
}
BOOL LLViewerTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, bool fast_update)
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height, fast_update) ;
}
void LLViewerTexture::setGLTextureCreated (bool initialized)
{
llassert(mGLTexturep.notNull()) ;
mGLTexturep->setGLTextureCreated (initialized) ;
}
void LLViewerTexture::setCategory(S32 category)
{
llassert(mGLTexturep.notNull()) ;
mGLTexturep->setCategory(category) ;
}
LLTexUnit::eTextureAddressMode LLViewerTexture::getAddressMode(void) const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getAddressMode() ;
}
S32 LLViewerTexture::getTextureMemory() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->mTextureMemory ;
}
LLGLenum LLViewerTexture::getPrimaryFormat() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getPrimaryFormat() ;
}
BOOL LLViewerTexture::getIsAlphaMask() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getIsAlphaMask() ;
}
BOOL LLViewerTexture::getMask(const LLVector2 &tc)
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getMask(tc) ;
}
F32 LLViewerTexture::getTimePassedSinceLastBound()
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getTimePassedSinceLastBound() ;
}
BOOL LLViewerTexture::getMissed() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getMissed() ;
}
BOOL LLViewerTexture::isJustBound() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->isJustBound() ;
}
void LLViewerTexture::forceUpdateBindStats(void) const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->forceUpdateBindStats() ;
}
/*U32 LLViewerTexture::getTexelsInAtlas() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getTexelsInAtlas() ;
}
U32 LLViewerTexture::getTexelsInGLTexture() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getTexelsInGLTexture() ;
}
BOOL LLViewerTexture::isGLTextureCreated() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->isGLTextureCreated() ;
}
S32 LLViewerTexture::getDiscardLevelInAtlas() const
{
llassert(mGLTexturep.notNull()) ;
return mGLTexturep->getDiscardLevelInAtlas() ;
}*/
void LLViewerTexture::destroyGLTexture()
{
if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture())
{
mGLTexturep->destroyGLTexture() ;
mTextureState = DELETED ;
}
}
void LLViewerTexture::setTexelsPerImage()
{
S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT);
S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT);
mTexelsPerImage = (F32)fullwidth * fullheight;
}
BOOL LLViewerTexture::isLargeImage()
{
return (S32)mTexelsPerImage > LLViewerTexture::sMinLargeImageSize ;
}
//virtual
void LLViewerTexture::updateBindStatsForTester()
{
/*LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
tester->updateTextureBindingStats(this) ;
}*/
}
//----------------------------------------------------------------------------------------------
//end of LLViewerTexture
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//start of LLViewerFetchedTexture
//----------------------------------------------------------------------------------------------
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps)
: LLViewerTexture(id, usemipmaps),
mTargetHost(host)
{
init(TRUE) ;
generateGLTexture() ;
}
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemipmaps)
: LLViewerTexture(raw, usemipmaps)
{
init(TRUE) ;
}
LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps)
: LLViewerTexture(id, usemipmaps),
mUrl(url)
{
init(TRUE) ;
generateGLTexture() ;
}
void LLViewerFetchedTexture::init(bool firstinit)
{
mOrigWidth = 0;
mOrigHeight = 0;
mNeedsAux = FALSE;
mRequestedDiscardLevel = -1;
mRequestedDownloadPriority = 0.f;
mFullyLoaded = FALSE;
mCanUseHTTP = true ;
mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1;
mDecodingAux = FALSE;
mKnownDrawWidth = 0;
mKnownDrawHeight = 0;
mKnownDrawSizeChanged = FALSE ;
if (firstinit)
{
mDecodePriority = 0.f;
mInImageList = 0;
}
// Only set mIsMissingAsset true when we know for certain that the database
// does not contain this image.
mIsMissingAsset = FALSE;
mLoadedCallbackDesiredDiscardLevel = S8_MAX;
mPauseLoadedCallBacks = TRUE ;
mNeedsCreateTexture = FALSE;
mIsRawImageValid = FALSE;
mRawDiscardLevel = INVALID_DISCARD_LEVEL;
mMinDiscardLevel = 0;
mHasFetcher = FALSE;
mIsFetching = FALSE;
mFetchState = 0;
mFetchPriority = 0;
mDownloadProgress = 0.f;
mFetchDeltaTime = 999999.f;
mRequestDeltaTime = 0.f;
mForSculpt = FALSE ;
mIsFetched = FALSE ;
if(!firstinit && mCachedRawImage.notNull())
mCachedRawImage->setInCache(false);
mCachedRawImage = NULL ;
mCachedRawDiscardLevel = -1 ;
mCachedRawImageReady = FALSE ;
mSavedRawImage = NULL ;
mForceToSaveRawImage = FALSE ;
mSaveRawImage = FALSE ;
mSavedRawDiscardLevel = -1 ;
mDesiredSavedRawDiscardLevel = -1 ;
mLastReferencedSavedRawImageTime = 0.0f ;
mLastCallBackActiveTime = 0.f;
}
LLViewerFetchedTexture::~LLViewerFetchedTexture()
{
//*NOTE getTextureFetch can return NULL when Viewer is shutting down.
// This is due to LLWearableList is singleton and is destroyed after
// LLAppViewer::cleanup() was called. (see ticket EXT-177)
if (mHasFetcher && LLAppViewer::getTextureFetch())
{
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
}
cleanup();
}
//virtual
S8 LLViewerFetchedTexture::getType() const
{
return LLViewerTexture::FETCHED_TEXTURE ;
}
void LLViewerFetchedTexture::cleanup()
{
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter++;
// We never finished loading the image. Indicate failure.
// Note: this allows mLoadedCallbackUserData to be cleaned up.
entryp->mCallback( FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData );
entryp->removeTexture(this) ;
delete entryp;
}
mLoadedCallbackList.clear();
mNeedsAux = FALSE;
// Clean up image data
destroyRawImage();
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(false);
mCachedRawImage = NULL ;
mCachedRawDiscardLevel = -1 ;
mCachedRawImageReady = FALSE ;
mSavedRawImage = NULL ;
}
void LLViewerFetchedTexture::setForSculpt()
{
static const S32 MAX_INTERVAL = 8 ; //frames
mForSculpt = TRUE ;
if(isForSculptOnly() && !getBoundRecently())
{
destroyGLTexture() ; //sculpt image does not need gl texture.
}
checkCachedRawSculptImage() ;
setMaxVirtualSizeResetInterval(MAX_INTERVAL) ;
}
BOOL LLViewerFetchedTexture::isForSculptOnly() const
{
return mForSculpt && !mNeedsGLTexture ;
}
BOOL LLViewerFetchedTexture::isDeleted()
{
return mTextureState == DELETED ;
}
BOOL LLViewerFetchedTexture::isInactive()
{
return mTextureState == INACTIVE ;
}
BOOL LLViewerFetchedTexture::isDeletionCandidate()
{
return mTextureState == DELETION_CANDIDATE ;
}
void LLViewerFetchedTexture::setDeletionCandidate()
{
if(mGLTexturep.notNull() && mGLTexturep->getTexName() && (mTextureState == INACTIVE))
{
mTextureState = DELETION_CANDIDATE ;
}
}
//set the texture inactive
void LLViewerFetchedTexture::setInactive()
{
if(mTextureState == ACTIVE && mGLTexturep.notNull() && mGLTexturep->getTexName() && !mGLTexturep->getBoundRecently())
{
mTextureState = INACTIVE ;
}
}
BOOL LLViewerFetchedTexture::isFullyLoaded() const
{
// Unfortunately, the boolean "mFullyLoaded" is never updated correctly so we use that logic
// to check if the texture is there and completely downloaded
return (mFullWidth != 0) && (mFullHeight != 0) && !mIsFetching && !mHasFetcher;
}
// virtual
void LLViewerFetchedTexture::dump()
{
LLViewerTexture::dump();
llinfos << "Dump : " << mID
<< ", mIsMissingAsset = " << (S32)mIsMissingAsset
<< ", mFullWidth = " << (S32)mFullWidth
<< ", mFullHeight = " << (S32)mFullHeight
<< ", mOrigWidth = " << (S32)mOrigWidth
<< ", mOrigHeight = " << (S32)mOrigHeight
<< llendl;
llinfos << " : "
<< " mFullyLoaded = " << (S32)mFullyLoaded
<< ", mFetchState = " << (S32)mFetchState
<< ", mFetchPriority = " << (S32)mFetchPriority
<< ", mDownloadProgress = " << (F32)mDownloadProgress
<< llendl;
llinfos << " : "
<< " mHasFetcher = " << (S32)mHasFetcher
<< ", mIsFetching = " << (S32)mIsFetching
<< ", mIsFetched = " << (S32)mIsFetched
<< ", mBoostLevel = " << (S32)mBoostLevel
<< llendl;
}
///////////////////////////////////////////////////////////////////////////////
// ONLY called from LLViewerFetchedTextureList
void LLViewerFetchedTexture::destroyTexture()
{
if(LLImageGL::sGlobalTextureMemoryInBytes < sMaxDesiredTextureMemInBytes)//not ready to release unused memory.
{
return ;
}
if (mNeedsCreateTexture)//return if in the process of generating a new texture.
{
return ;
}
destroyGLTexture() ;
mFullyLoaded = FALSE ;
}
void LLViewerFetchedTexture::addToCreateTexture()
{
bool force_update = false ;
if (getComponents() != mRawImage->getComponents())
{
// We've changed the number of components, so we need to move any
// objects using this pool to a different pool.
mComponents = mRawImage->getComponents();
mGLTexturep->setComponents(mComponents) ;
force_update = true ;
for(U32 i = 0 ; i < mNumFaces ; i++)
{
mFaceList[i]->dirtyTexture() ;
}
//discard the cached raw image and the saved raw image
mCachedRawImageReady = FALSE ;
mCachedRawDiscardLevel = -1 ;
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(false);
mCachedRawImage = NULL ;
mSavedRawDiscardLevel = -1 ;
mSavedRawImage = NULL ;
}
if(isForSculptOnly())
{
//just update some variables, not to create a real GL texture.
createGLTexture(mRawDiscardLevel, mRawImage, 0, FALSE) ;
mNeedsCreateTexture = FALSE ;
destroyRawImage();
}
else if(!force_update && getDiscardLevel() > -1 && getDiscardLevel() <= mRawDiscardLevel)
{
mNeedsCreateTexture = FALSE ;
destroyRawImage();
}
else
{
#if 1
//
//if mRequestedDiscardLevel > mDesiredDiscardLevel, we assume the required image res keep going up,
//so do not scale down the over qualified image.
//Note: scaling down image is expensensive. Do it only when very necessary.
//
if(mRequestedDiscardLevel <= mDesiredDiscardLevel && !mForceToSaveRawImage)
{
S32 w = mFullWidth >> mRawDiscardLevel;
S32 h = mFullHeight >> mRawDiscardLevel;
//if big image, do not load extra data
//scale it down to size >= LLViewerTexture::sMinLargeImageSize
if(w * h > LLViewerTexture::sMinLargeImageSize)
{
S32 d_level = llmin(mRequestedDiscardLevel, (S32)mDesiredDiscardLevel) - mRawDiscardLevel ;
if(d_level > 0)
{
S32 i = 0 ;
while((d_level > 0) && ((w >> i) * (h >> i) > LLViewerTexture::sMinLargeImageSize))
{
i++;
d_level--;
}
if(i > 0)
{
mRawDiscardLevel += i ;
if(mRawDiscardLevel >= getDiscardLevel() && getDiscardLevel() > 0)
{
mNeedsCreateTexture = FALSE ;
destroyRawImage();
return ;
}
mRawImage->scale(w >> i, h >> i) ;
}
}
}
}
#endif
mNeedsCreateTexture = TRUE;
gTextureList.mCreateTextureList.insert(this);
}
return ;
}
// ONLY called from LLViewerTextureList
BOOL LLViewerFetchedTexture::createTexture(S32 usename/*= 0*/)
{
if (!mNeedsCreateTexture)
{
destroyRawImage();
return FALSE;
}
mNeedsCreateTexture = FALSE;
if (mRawImage.isNull())
{
llerrs << "LLViewerTexture trying to create texture with no Raw Image" << llendl;
}
// llinfos << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ",
// mRawDiscardLevel,
// mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize())
// << mID.getString() << llendl;
BOOL res = TRUE;
if (!gNoRender)
{
// store original size only for locally-sourced images
if (mUrl.compare(0, 7, "file://") == 0)
{
mOrigWidth = mRawImage->getWidth();
mOrigHeight = mRawImage->getHeight();
// leave black border, do not scale image content
mRawImage->expandToPowerOfTwo(MAX_IMAGE_SIZE, FALSE);
mFullWidth = mRawImage->getWidth();
mFullHeight = mRawImage->getHeight();
setTexelsPerImage();
}
else
{
mOrigWidth = mFullWidth;
mOrigHeight = mFullHeight;
}
bool size_okay = true;
U32 raw_width = mRawImage->getWidth() << mRawDiscardLevel;
U32 raw_height = mRawImage->getHeight() << mRawDiscardLevel;
if( raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE )
{
llinfos << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << llendl;
size_okay = false;
}
if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight()))
{
// A non power-of-two image was uploaded (through a non standard client)
llinfos << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << llendl;
size_okay = false;
}
if( !size_okay )
{
// An inappropriately-sized image was uploaded (through a non standard client)
// We treat these images as missing assets which causes them to
// be renderd as 'missing image' and to stop requesting data
setIsMissingAsset();
destroyRawImage();
return FALSE;
}
//if(!(res = insertToAtlas()))
//{
res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
//resetFaceAtlas() ;
//}
setActive() ;
}
if (!needsToSaveRawImage())
{
mNeedsAux = FALSE;
destroyRawImage();
}
return res;
}
// Call with 0,0 to turn this feature off.
//virtual
void LLViewerFetchedTexture::setKnownDrawSize(S32 width, S32 height)
{
if(mKnownDrawWidth < width || mKnownDrawHeight < height)
{
mKnownDrawWidth = llmax(mKnownDrawWidth, width) ;
mKnownDrawHeight = llmax(mKnownDrawHeight, height) ;
mKnownDrawSizeChanged = TRUE ;
mFullyLoaded = FALSE ;
}
addTextureStats((F32)(mKnownDrawWidth * mKnownDrawHeight));
}
//virtual
void LLViewerFetchedTexture::processTextureStats()
{
if(mFullyLoaded)
{
if(mDesiredDiscardLevel > mMinDesiredDiscardLevel)//need to load more
{
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel) ;
mFullyLoaded = FALSE ;
}
}
else
{
updateVirtualSize() ;
static LLCachedControl<bool> textures_fullres("TextureLoadFullRes",false);
if (textures_fullres)
{
mDesiredDiscardLevel = 0;
}
else if(!mFullWidth || !mFullHeight)
{
mDesiredDiscardLevel = llmin(getMaxDiscardLevel(), (S32)mLoadedCallbackDesiredDiscardLevel) ;
}
else
{
if(!mKnownDrawWidth || !mKnownDrawHeight || mFullWidth <= mKnownDrawWidth || mFullHeight <= mKnownDrawHeight)
{
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
{
mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
}
else
{
mDesiredDiscardLevel = 0;
}
}
else if(mKnownDrawSizeChanged)//known draw size is set
{
mDesiredDiscardLevel = (S8)llmin(log((F32)mFullWidth / mKnownDrawWidth) / log_2,
log((F32)mFullHeight / mKnownDrawHeight) / log_2) ;
mDesiredDiscardLevel = llclamp(mDesiredDiscardLevel, (S8)0, (S8)getMaxDiscardLevel()) ;
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel) ;
}
mKnownDrawSizeChanged = FALSE ;
if(getDiscardLevel() >= 0 && (getDiscardLevel() <= mDesiredDiscardLevel))
{
mFullyLoaded = TRUE ;
}
}
}
if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0) //force to refetch the texture.
{
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S8)mDesiredSavedRawDiscardLevel) ;
if(getDiscardLevel() < 0 || getDiscardLevel() > mDesiredDiscardLevel)
{
mFullyLoaded = FALSE ;
}
}
}
const F32 MAX_PRIORITY_PIXEL = 999.f ; //pixel area
const F32 PRIORITY_BOOST_LEVEL_FACTOR = 1000.f ; //boost level
const F32 PRIORITY_DELTA_DISCARD_LEVEL_FACTOR = 100000.f ; //delta discard
const S32 MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY = 4 ;
const F32 PRIORITY_ADDITIONAL_FACTOR = 1000000.f ; //additional
const S32 MAX_ADDITIONAL_LEVEL_FOR_PRIORITY = 8 ;
const F32 PRIORITY_BOOST_HIGH_FACTOR = 10000000.f ;//boost high
F32 LLViewerFetchedTexture::calcDecodePriority()
{
#ifndef LL_RELEASE_FOR_DOWNLOAD
if (mID == LLAppViewer::getTextureFetch()->mDebugID)
{
LLAppViewer::getTextureFetch()->mDebugCount++; // for setting breakpoints
}
#endif
if (mNeedsCreateTexture)
{
return mDecodePriority; // no change while waiting to create
}
if(mFullyLoaded && !mForceToSaveRawImage)//already loaded for static texture
{
return -1.0f ; //alreay fetched
}
S32 cur_discard = getCurrentDiscardLevelForFetching();
bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel));
F32 pixel_priority = fsqrtf(mMaxVirtualSize);
F32 priority = 0.f;
if (mIsMissingAsset)
{
priority = 0.0f;
}
else if(mDesiredDiscardLevel >= cur_discard && cur_discard > -1)
{
priority = -2.0f ;
}
else if(mCachedRawDiscardLevel > -1 && mDesiredDiscardLevel >= mCachedRawDiscardLevel)
{
priority = -3.0f;
}
else if (mDesiredDiscardLevel > getMaxDiscardLevel())
{
// Don't decode anything we don't need
priority = -4.0f;
}
else if ((mBoostLevel == LLViewerTexture::BOOST_UI || mBoostLevel == LLViewerTexture::BOOST_ICON) && !have_all_data)
{
priority = 1.f;
}
else if (pixel_priority < 0.001f && !have_all_data)
{
// Not on screen but we might want some data
if (mBoostLevel > BOOST_HIGH)
{
// Always want high boosted images
priority = 1.f;
}
else
{
priority = -5.f; //stop fetching
}
}
else if (cur_discard < 0)
{
//texture does not have any data, so we don't know the size of the image, treat it like 32 * 32.
// priority range = 100,000 - 500,000
static const F64 log_2 = log(2.0);
F32 desired = (F32)(log(32.0/pixel_priority) / log_2);
S32 ddiscard = MAX_DISCARD_LEVEL - (S32)desired;
ddiscard = llclamp(ddiscard, 0, MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY);
priority = (ddiscard + 1) * PRIORITY_DELTA_DISCARD_LEVEL_FACTOR;
setAdditionalDecodePriority(1.0f) ;//boost the textures without any data so far.
}
else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel))
{
// larger mips are corrupted
priority = -6.0f;
}
else
{
// priority range = 100,000 - 500,000
S32 desired_discard = mDesiredDiscardLevel;
if (!isJustBound() && mCachedRawImageReady)
{
if(mBoostLevel < BOOST_HIGH)
{
// We haven't rendered this in a while, de-prioritize it
desired_discard += 2;
}
else
{
// We haven't rendered this in the last half second, and we have a cached raw image, leave the desired discard as-is
desired_discard = cur_discard;
}
}
S32 ddiscard = cur_discard - desired_discard;
ddiscard = llclamp(ddiscard, -1, MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY);
priority = (ddiscard + 1) * PRIORITY_DELTA_DISCARD_LEVEL_FACTOR;
}
// Priority Formula:
// BOOST_HIGH + ADDITIONAL PRI + DELTA DISCARD + BOOST LEVEL + PIXELS
// [10,000,000] + [1,000,000-9,000,000] + [100,000-500,000] + [1-20,000] + [0-999]
if (priority > 0.0f)
{
bool large_enough = mCachedRawImageReady && ((S32)mTexelsPerImage > sMinLargeImageSize) ;
if(large_enough)
{
//Note:
//to give small, low-priority textures some chance to be fetched,
//cut the priority in half if the texture size is larger than 256 * 256 and has a 64*64 ready.
priority *= 0.5f ;
}
pixel_priority = llclamp(pixel_priority, 0.0f, MAX_PRIORITY_PIXEL);
priority += pixel_priority + PRIORITY_BOOST_LEVEL_FACTOR * mBoostLevel;
if ( mBoostLevel > BOOST_HIGH)
{
if(mBoostLevel > BOOST_SUPER_HIGH)
{
//for very important textures, always grant the highest priority.
priority += PRIORITY_BOOST_HIGH_FACTOR;
}
else if(mCachedRawImageReady)
{
//Note:
//to give small, low-priority textures some chance to be fetched,
//if high priority texture has a 64*64 ready, lower its fetching priority.
setAdditionalDecodePriority(0.5f) ;
}
else
{
priority += PRIORITY_BOOST_HIGH_FACTOR;
}
}
if(mAdditionalDecodePriority > 0.0f)
{
// priority range += 1,000,000.f-9,000,000.f
F32 additional = PRIORITY_ADDITIONAL_FACTOR * (1.0 + mAdditionalDecodePriority * MAX_ADDITIONAL_LEVEL_FOR_PRIORITY);
if(large_enough)
{
//Note:
//to give small, low-priority textures some chance to be fetched,
//cut the additional priority to a quarter if the texture size is larger than 256 * 256 and has a 64*64 ready.
additional *= 0.25f ;
}
priority += additional;
}
}
return priority;
}
//static
F32 LLViewerFetchedTexture::maxDecodePriority()
{
static const F32 max_priority = PRIORITY_BOOST_HIGH_FACTOR + //boost_high
PRIORITY_ADDITIONAL_FACTOR * (MAX_ADDITIONAL_LEVEL_FOR_PRIORITY + 1) + //additional (view dependent factors)
PRIORITY_DELTA_DISCARD_LEVEL_FACTOR * (MAX_DELTA_DISCARD_LEVEL_FOR_PRIORITY + 1) + //delta discard
PRIORITY_BOOST_LEVEL_FACTOR * (BOOST_MAX_LEVEL - 1) + //boost level
MAX_PRIORITY_PIXEL + 1.0f ; //pixel area.
return max_priority ;
}
//============================================================================
void LLViewerFetchedTexture::setDecodePriority(F32 priority)
{
llassert(!mInImageList);
mDecodePriority = priority;
if(mDecodePriority < F_ALMOST_ZERO)
{
mStopFetchingTimer.reset() ;
}
}
void LLViewerFetchedTexture::setAdditionalDecodePriority(F32 priority)
{
priority = llclamp(priority, 0.f, 1.f);
if(mAdditionalDecodePriority < priority)
{
mAdditionalDecodePriority = priority;
}
}
void LLViewerFetchedTexture::updateVirtualSize()
{
if(!mMaxVirtualSizeResetCounter)
{
addTextureStats(0.f, FALSE) ;//reset
}
for(U32 i = 0 ; i < mNumFaces ; i++)
{
LLFace* facep = mFaceList[i] ;
if(facep->getDrawable()->isRecentlyVisible())
{
addTextureStats(facep->getVirtualSize()) ;
setAdditionalDecodePriority(facep->getImportanceToCamera()) ;
}
}
if(mMaxVirtualSizeResetCounter > 0)
{
mMaxVirtualSizeResetCounter--;
}
reorganizeFaceList() ;
reorganizeVolumeList();
}
S32 LLViewerFetchedTexture::getCurrentDiscardLevelForFetching()
{
S32 current_discard = getDiscardLevel() ;
if(mForceToSaveRawImage)
{
if(mSavedRawDiscardLevel < 0 || current_discard < 0)
{
current_discard = -1 ;
}
else
{
current_discard = llmax(current_discard, mSavedRawDiscardLevel) ;
}
}
return current_discard ;
}
bool LLViewerFetchedTexture::updateFetch()
{
static LLCachedControl<bool> textures_decode_disabled("TextureDecodeDisabled",false);
if(textures_decode_disabled)
{
return false ;
}
mFetchState = 0;
mFetchPriority = 0;
mFetchDeltaTime = 999999.f;
mRequestDeltaTime = 999999.f;
#ifndef LL_RELEASE_FOR_DOWNLOAD
if (mID == LLAppViewer::getTextureFetch()->mDebugID)
{
LLAppViewer::getTextureFetch()->mDebugCount++; // for setting breakpoints
}
#endif
if (mNeedsCreateTexture)
{
// We may be fetching still (e.g. waiting on write)
// but don't check until we've processed the raw data we have
return false;
}
if (mIsMissingAsset)
{
llassert_always(!mHasFetcher);
return false; // skip
}
if (!mLoadedCallbackList.empty() && mRawImage.notNull())
{
return false; // process any raw image data in callbacks before replacing
}
S32 current_discard = getCurrentDiscardLevelForFetching() ;
S32 desired_discard = getDesiredDiscardLevel();
F32 decode_priority = getDecodePriority();
decode_priority = llclamp(decode_priority, 0.0f, maxDecodePriority());
if (mIsFetching)
{
// Sets mRawDiscardLevel, mRawImage, mAuxRawImage
S32 fetch_discard = current_discard;
if (mRawImage.notNull()) sRawCount--;
if (mAuxRawImage.notNull()) sAuxCount--;
bool finished = LLAppViewer::getTextureFetch()->getRequestFinished(getID(), fetch_discard, mRawImage, mAuxRawImage);
if (mRawImage.notNull()) sRawCount++;
if (mAuxRawImage.notNull()) sAuxCount++;
if (finished)
{
mIsFetching = FALSE;
mLastPacketTimer.reset() ;
}
else
{
mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP);
}
// We may have data ready regardless of whether or not we are finished (e.g. waiting on write)
if (mRawImage.notNull())
{
/*LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
mIsFetched = TRUE ;
tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)) ;
}*/
mRawDiscardLevel = fetch_discard;
if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) &&
(current_discard < 0 || mRawDiscardLevel < current_discard))
{
mFullWidth = mRawImage->getWidth() << mRawDiscardLevel;
mFullHeight = mRawImage->getHeight() << mRawDiscardLevel;
setTexelsPerImage();
if(mFullWidth > MAX_IMAGE_SIZE || mFullHeight > MAX_IMAGE_SIZE)
{
//discard all oversized textures.
destroyRawImage();
setIsMissingAsset();
mRawDiscardLevel = INVALID_DISCARD_LEVEL ;
mIsFetching = FALSE ;
mLastPacketTimer.reset();
}
else
{
mIsRawImageValid = TRUE;
addToCreateTexture() ;
}
return TRUE ;
}
else
{
// Data is ready but we don't need it
// (received it already while fetcher was writing to disk)
destroyRawImage();
return false; // done
}
}
if (!mIsFetching)
{
if ((decode_priority > 0) && (mRawDiscardLevel < 0 || mRawDiscardLevel == INVALID_DISCARD_LEVEL))
{
// We finished but received no data
if (current_discard < 0)
{
setIsMissingAsset();
desired_discard = -1;
}
else
{
//llwarns << mID << ": Setting min discard to " << current_discard << llendl;
mMinDiscardLevel = current_discard;
desired_discard = current_discard;
}
destroyRawImage();
}
else if (mRawImage.notNull())
{
// We have data, but our fetch failed to return raw data
// *TODO: FIgure out why this is happening and fix it
destroyRawImage();
}
}
else
{
// // Useful debugging code for undesired deprioritization of textures.
// if (decode_priority <= 0.0f && desired_discard >= 0 && desired_discard < current_discard)
// {
// llinfos << "Calling updateRequestPriority() with decode_priority = 0.0f" << llendl;
// calcDecodePriority();
// }
static const F32 MAX_HOLD_TIME = 5.0f ; //seconds to wait before canceling fecthing if decode_priority is 0.f.
if(decode_priority > 0.0f || mStopFetchingTimer.getElapsedTimeF32() > MAX_HOLD_TIME)
{
mStopFetchingTimer.reset() ;
LLAppViewer::getTextureFetch()->updateRequestPriority(mID, decode_priority);
}
}
}
bool make_request = true;
if (decode_priority <= 0)
{
make_request = false;
}
else if (mNeedsCreateTexture || mIsMissingAsset)
{
make_request = false;
}
else if (current_discard >= 0 && current_discard <= mMinDiscardLevel)
{
make_request = false;
}
//else if (!isJustBound() && mCachedRawImageReady)
//{
// make_request = false;
//}
if(make_request)
{
//load the texture progressively.
S32 delta_level = (mBoostLevel > LLViewerTexture::BOOST_NONE) ? 2 : 1 ;
if(current_discard < 0)
{
desired_discard = llmax(desired_discard, getMaxDiscardLevel() - delta_level);
}
else
{
desired_discard = llmax(desired_discard, current_discard - delta_level);
}
if (mIsFetching)
{
if (mRequestedDiscardLevel <= desired_discard)
{
make_request = false;
}
}
else
{
if (current_discard >= 0 && current_discard <= desired_discard)
{
make_request = false;
}
}
}
if (make_request)
{
S32 w=0, h=0, c=0;
if (getDiscardLevel() >= 0)
{
w = mGLTexturep->getWidth(0);
h = mGLTexturep->getHeight(0);
c = mComponents;
}
/*const U32 override_tex_discard_level = gSavedSettings.getU32("TextureDiscardLevel");
if (override_tex_discard_level != 0)
{
desired_discard = override_tex_discard_level;
}*/
// bypass texturefetch directly by pulling from LLTextureCache
bool fetch_request_created = false;
fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mUrl, getID(),getTargetHost(), decode_priority,
w, h, c, desired_discard, needsAux(), mCanUseHTTP);
if (fetch_request_created)
{
mHasFetcher = TRUE;
mIsFetching = TRUE;
mRequestedDiscardLevel = desired_discard;
mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority,
mFetchPriority, mFetchDeltaTime, mRequestDeltaTime, mCanUseHTTP);
}
// if createRequest() failed, we're finishing up a request for this UUID,
// wait for it to complete
}
else if (mHasFetcher && !mIsFetching)
{
// Only delete requests that haven't receeived any network data for a while
const F32 FETCH_IDLE_TIME = 5.f;
if (mLastPacketTimer.getElapsedTimeF32() > FETCH_IDLE_TIME)
{
// llinfos << "Deleting request: " << getID() << " Discard: " << current_discard << " <= min:" << mMinDiscardLevel << " or priority == 0: " << decode_priority << llendl;
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
mHasFetcher = FALSE;
}
}
llassert_always(mRawImage.notNull() || (!mNeedsCreateTexture && !mIsRawImageValid));
return mIsFetching ? true : false;
}
void LLViewerFetchedTexture::setIsMissingAsset()
{
if (mUrl.empty())
{
llwarns << mID << ": Marking image as missing" << llendl;
}
else
{
llwarns << mUrl << ": Marking image as missing" << llendl;
}
if (mHasFetcher)
{
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
mHasFetcher = FALSE;
mIsFetching = FALSE;
mLastPacketTimer.reset();
mFetchState = 0;
mFetchPriority = 0;
}
mIsMissingAsset = TRUE;
}
void LLViewerFetchedTexture::setLoadedCallback( loaded_callback_func loaded_callback,
S32 discard_level, BOOL keep_imageraw, BOOL needs_aux, void* userdata,
LLLoadedCallbackEntry::source_callback_list_t* src_callback_list, BOOL pause)
{
//
// Don't do ANYTHING here, just add it to the global callback list
//
if (mLoadedCallbackList.empty())
{
// Put in list to call this->doLoadedCallbacks() periodically
gTextureList.mCallbackList.insert(this);
mLoadedCallbackDesiredDiscardLevel = (S8)discard_level;
}
else
{
mLoadedCallbackDesiredDiscardLevel = llmin(mLoadedCallbackDesiredDiscardLevel, (S8)discard_level) ;
}
if(mPauseLoadedCallBacks && !pause)
{
unpauseLoadedCallbacks(src_callback_list) ;
}
LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata, src_callback_list, this, pause);
mLoadedCallbackList.push_back(entryp);
mNeedsAux |= needs_aux;
if(keep_imageraw)
{
mSaveRawImage = TRUE ;
}
if (mNeedsAux && mAuxRawImage.isNull() && getDiscardLevel() >= 0)
{
// We need aux data, but we've already loaded the image, and it didn't have any
llwarns << "No aux data available for callback for image:" << getID() << llendl;
}
mLastCallBackActiveTime = sCurrentTime ;
}
void LLViewerFetchedTexture::clearCallbackEntryList()
{
if(mLoadedCallbackList.empty())
{
return ;
}
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter;
// We never finished loading the image. Indicate failure.
// Note: this allows mLoadedCallbackUserData to be cleaned up.
entryp->mCallback(FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData);
iter = mLoadedCallbackList.erase(iter) ;
delete entryp;
}
gTextureList.mCallbackList.erase(this);
mLoadedCallbackDesiredDiscardLevel = S8_MAX ;
if(needsToSaveRawImage())
{
destroySavedRawImage() ;
}
return ;
}
void LLViewerFetchedTexture::deleteCallbackEntry(const LLLoadedCallbackEntry::source_callback_list_t* callback_list)
{
if(mLoadedCallbackList.empty() || !callback_list)
{
return ;
}
S32 desired_discard = S8_MAX ;
S32 desired_raw_discard = INVALID_DISCARD_LEVEL ;
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter;
if(entryp->mSourceCallbackList == callback_list)
{
// We never finished loading the image. Indicate failure.
// Note: this allows mLoadedCallbackUserData to be cleaned up.
entryp->mCallback(FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData);
iter = mLoadedCallbackList.erase(iter) ;
delete entryp;
}
else
{
++iter;
desired_discard = llmin(desired_discard, entryp->mDesiredDiscard) ;
if(entryp->mNeedsImageRaw)
{
desired_raw_discard = llmin(desired_raw_discard, entryp->mDesiredDiscard) ;
}
}
}
mLoadedCallbackDesiredDiscardLevel = desired_discard;
if (mLoadedCallbackList.empty())
{
// If we have no callbacks, take us off of the image callback list.
gTextureList.mCallbackList.erase(this);
if(needsToSaveRawImage())
{
destroySavedRawImage() ;
}
}
else if(needsToSaveRawImage() && mBoostLevel != LLViewerTexture::BOOST_PREVIEW)
{
if(desired_raw_discard != INVALID_DISCARD_LEVEL)
{
mDesiredSavedRawDiscardLevel = desired_raw_discard ;
}
else
{
destroySavedRawImage() ;
}
}
}
void LLViewerFetchedTexture::unpauseLoadedCallbacks(const LLLoadedCallbackEntry::source_callback_list_t* callback_list)
{
if(!callback_list)
{
mPauseLoadedCallBacks = FALSE ;
return ;
}
BOOL need_raw = FALSE ;
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter++;
if(entryp->mSourceCallbackList == callback_list)
{
entryp->mPaused = FALSE ;
if(entryp->mNeedsImageRaw)
{
need_raw = TRUE ;
}
}
}
mPauseLoadedCallBacks = FALSE ;
if(need_raw)
{
mSaveRawImage = TRUE ;
}
}
void LLViewerFetchedTexture::pauseLoadedCallbacks(const LLLoadedCallbackEntry::source_callback_list_t* callback_list)
{
if(!callback_list)
{
return ;
}
bool paused = true ;
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter++;
if(entryp->mSourceCallbackList == callback_list)
{
entryp->mPaused = TRUE ;
}
else if(!entryp->mPaused)
{
paused = false ;
}
}
if(paused)
{
mPauseLoadedCallBacks = TRUE ;//when set, loaded callback is paused.
resetTextureStats();
mSaveRawImage = FALSE ;
}
}
bool LLViewerFetchedTexture::doLoadedCallbacks()
{
static const F32 MAX_INACTIVE_TIME = 120.f ; //seconds
if (mNeedsCreateTexture)
{
return false;
}
if(sCurrentTime - mLastCallBackActiveTime > MAX_INACTIVE_TIME)
{
clearCallbackEntryList() ; //remove all callbacks.
return false ;
}
bool res = false;
if (isMissingAsset())
{
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter++;
// We never finished loading the image. Indicate failure.
// Note: this allows mLoadedCallbackUserData to be cleaned up.
entryp->mCallback(FALSE, this, NULL, NULL, 0, TRUE, entryp->mUserData);
delete entryp;
}
mLoadedCallbackList.clear();
// Remove ourself from the global list of textures with callbacks
gTextureList.mCallbackList.erase(this);
}
if(mPauseLoadedCallBacks)
{
destroyRawImage();
return res; //paused
}
S32 gl_discard = getDiscardLevel();
// If we don't have a legit GL image, set it to be lower than the worst discard level
if (gl_discard == -1)
{
gl_discard = MAX_DISCARD_LEVEL + 1;
}
//
// Determine the quality levels of textures that we can provide to callbacks
// and whether we need to do decompression/readback to get it
//
S32 current_raw_discard = MAX_DISCARD_LEVEL + 1; // We can always do a readback to get a raw discard
S32 best_raw_discard = gl_discard; // Current GL quality level
S32 current_aux_discard = MAX_DISCARD_LEVEL + 1;
S32 best_aux_discard = MAX_DISCARD_LEVEL + 1;
if (mIsRawImageValid)
{
// If we have an existing raw image, we have a baseline for the raw and auxiliary quality levels.
best_raw_discard = llmin(best_raw_discard, mRawDiscardLevel);
best_aux_discard = llmin(best_aux_discard, mRawDiscardLevel); // We always decode the aux when we decode the base raw
current_aux_discard = llmin(current_aux_discard, best_aux_discard);
}
else
{
// We have no data at all, we need to get it
// Do this by forcing the best aux discard to be 0.
best_aux_discard = 0;
}
//
// See if any of the callbacks would actually run using the data that we can provide,
// and also determine if we need to perform any readbacks or decodes.
//
bool run_gl_callbacks = false;
bool run_raw_callbacks = false;
bool need_readback = false;
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
LLLoadedCallbackEntry *entryp = *iter++;
if (entryp->mNeedsImageRaw)
{
if (mNeedsAux)
{
//
// Need raw and auxiliary channels
//
if (entryp->mLastUsedDiscard > current_aux_discard)
{
// We have useful data, run the callbacks
run_raw_callbacks = true;
}
}
else
{
if (entryp->mLastUsedDiscard > current_raw_discard)
{
// We have useful data, just run the callbacks
run_raw_callbacks = true;
}
else if (entryp->mLastUsedDiscard > best_raw_discard)
{
// We can readback data, and then run the callbacks
need_readback = true;
run_raw_callbacks = true;
}
}
}
else
{
// Needs just GL
if (entryp->mLastUsedDiscard > gl_discard)
{
// We have enough data, run this callback requiring GL data
run_gl_callbacks = true;
}
}
}
//
// Do a readback if required, OR start off a texture decode
//
if (need_readback && (getMaxDiscardLevel() > gl_discard))
{
// Do a readback to get the GL data into the raw image
// We have GL data.
destroyRawImage();
reloadRawImage(mLoadedCallbackDesiredDiscardLevel);
llassert_always(mRawImage.notNull());
llassert_always(!mNeedsAux || mAuxRawImage.notNull());
}
//
// Run raw/auxiliary data callbacks
//
if (run_raw_callbacks && mIsRawImageValid && (mRawDiscardLevel <= getMaxDiscardLevel()))
{
// Do callbacks which require raw image data.
//llinfos << "doLoadedCallbacks raw for " << getID() << llendl;
// Call each party interested in the raw data.
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
callback_list_t::iterator curiter = iter++;
LLLoadedCallbackEntry *entryp = *curiter;
if (entryp->mNeedsImageRaw && (entryp->mLastUsedDiscard > mRawDiscardLevel))
{
// If we've loaded all the data there is to load or we've loaded enough
// to satisfy the interested party, then this is the last time that
// we're going to call them.
mLastCallBackActiveTime = sCurrentTime ;
//llassert_always(mRawImage.notNull());
if(mNeedsAux && mAuxRawImage.isNull())
{
llwarns << "Raw Image with no Aux Data for callback" << llendl;
}
BOOL final = mRawDiscardLevel <= entryp->mDesiredDiscard ? TRUE : FALSE;
//llinfos << "Running callback for " << getID() << llendl;
//llinfos << mRawImage->getWidth() << "x" << mRawImage->getHeight() << llendl;
entryp->mLastUsedDiscard = mRawDiscardLevel;
entryp->mCallback(TRUE, this, mRawImage, mAuxRawImage, mRawDiscardLevel, final, entryp->mUserData);
if (final)
{
iter = mLoadedCallbackList.erase(curiter);
delete entryp;
}
res = true;
}
}
}
//
// Run GL callbacks
//
if (run_gl_callbacks && (gl_discard <= getMaxDiscardLevel()))
{
//llinfos << "doLoadedCallbacks GL for " << getID() << llendl;
// Call the callbacks interested in GL data.
for(callback_list_t::iterator iter = mLoadedCallbackList.begin();
iter != mLoadedCallbackList.end(); )
{
callback_list_t::iterator curiter = iter++;
LLLoadedCallbackEntry *entryp = *curiter;
if (!entryp->mNeedsImageRaw && (entryp->mLastUsedDiscard > gl_discard))
{
mLastCallBackActiveTime = sCurrentTime ;
BOOL final = gl_discard <= entryp->mDesiredDiscard ? TRUE : FALSE;
entryp->mLastUsedDiscard = gl_discard;
entryp->mCallback(TRUE, this, NULL, NULL, gl_discard, final, entryp->mUserData);
if (final)
{
iter = mLoadedCallbackList.erase(curiter);
delete entryp;
}
res = true;
}
}
}
//
// If we have no callbacks, take us off of the image callback list.
//
if (mLoadedCallbackList.empty())
{
gTextureList.mCallbackList.erase(this);
}
// Done with any raw image data at this point (will be re-created if we still have callbacks)
destroyRawImage();
return res;
}
//virtual
void LLViewerFetchedTexture::forceImmediateUpdate()
{
//only immediately update a deleted texture which is now being re-used.
if(!isDeleted())
{
return ;
}
//if already called forceImmediateUpdate()
if(mInImageList && mDecodePriority == LLViewerFetchedTexture::maxDecodePriority())
{
return ;
}
gTextureList.forceImmediateUpdate(this) ;
return ;
}
LLImageRaw* LLViewerFetchedTexture::reloadRawImage(S8 discard_level)
{
llassert_always(mGLTexturep.notNull()) ;
llassert_always(discard_level >= 0);
llassert_always(mComponents > 0);
if (mRawImage.notNull())
{
//mRawImage is in use by somebody else, do not delete it.
return NULL ;
}
if(mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= discard_level)
{
if(mSavedRawDiscardLevel != discard_level)
{
mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ;
mRawImage->copy(getSavedRawImage()) ;
}
else
{
mRawImage = getSavedRawImage() ;
}
mRawDiscardLevel = discard_level ;
}
else
{
//force to fetch raw image again if cached raw image is not good enough.
if(mCachedRawDiscardLevel > discard_level)
{
mRawImage = mCachedRawImage ;
mRawDiscardLevel = mCachedRawDiscardLevel;
}
else //cached raw image is good enough, copy it.
{
if(mCachedRawDiscardLevel != discard_level)
{
mRawImage = new LLImageRaw(getWidth(discard_level), getHeight(discard_level), getComponents()) ;
mRawImage->copy(mCachedRawImage) ;
}
else
{
mRawImage = mCachedRawImage ;
}
mRawDiscardLevel = discard_level ;
}
}
mIsRawImageValid = TRUE ;
sRawCount++;
return mRawImage;
}
bool LLViewerFetchedTexture::needsToSaveRawImage()
{
return mForceToSaveRawImage || mSaveRawImage ;
}
void LLViewerFetchedTexture::destroyRawImage()
{
if (mAuxRawImage.notNull()) sAuxCount--;
if (mRawImage.notNull())
{
sRawCount--;
if(mIsRawImageValid)
{
if(needsToSaveRawImage())
{
saveRawImage() ;
}
setCachedRawImage() ;
}
}
mRawImage = NULL;
mAuxRawImage = NULL;
mIsRawImageValid = FALSE;
mRawDiscardLevel = INVALID_DISCARD_LEVEL;
}
//use the mCachedRawImage to (re)generate the gl texture.
//virtual
void LLViewerFetchedTexture::switchToCachedImage()
{
if(mCachedRawImage.notNull())
{
mRawImage = mCachedRawImage ;
if (getComponents() != mRawImage->getComponents())
{
// We've changed the number of components, so we need to move any
// objects using this pool to a different pool.
mComponents = mRawImage->getComponents();
mGLTexturep->setComponents(mComponents) ;
gTextureList.dirtyImage(this);
}
mIsRawImageValid = TRUE;
mRawDiscardLevel = mCachedRawDiscardLevel ;
gTextureList.mCreateTextureList.insert(this);
mNeedsCreateTexture = TRUE;
}
}
//cache the imageraw forcefully.
//virtual
void LLViewerFetchedTexture::setCachedRawImage(S32 discard_level, LLImageRaw* imageraw)
{
if(imageraw != mRawImage.get())
{
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(false);
mCachedRawImage = imageraw ;
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(true);
mCachedRawDiscardLevel = discard_level ;
mCachedRawImageReady = TRUE ;
}
}
void LLViewerFetchedTexture::setCachedRawImage()
{
if(mRawImage == mCachedRawImage)
{
return ;
}
if(!mIsRawImageValid)
{
return ;
}
if(mCachedRawImageReady)
{
return ;
}
if(mCachedRawDiscardLevel < 0 || mCachedRawDiscardLevel > mRawDiscardLevel)
{
S32 i = 0 ;
S32 w = mRawImage->getWidth() ;
S32 h = mRawImage->getHeight() ;
S32 max_size = MAX_CACHED_RAW_IMAGE_AREA ;
if(LLViewerTexture::BOOST_TERRAIN == mBoostLevel)
{
max_size = MAX_CACHED_RAW_TERRAIN_IMAGE_AREA ;
}
if(mForSculpt)
{
max_size = MAX_CACHED_RAW_SCULPT_IMAGE_AREA ;
mCachedRawImageReady = !mRawDiscardLevel ;
}
else
{
mCachedRawImageReady = (!mRawDiscardLevel || ((w * h) >= max_size)) ;
}
while(((w >> i) * (h >> i)) > max_size)
{
++i ;
}
if(i)
{
if(!(w >> i) || !(h >> i))
{
--i ;
}
if (mRawImage->getComponents() == 5)
{
llwarns << "IMP-582: Trying to scale an image (" << mID << ") with 5 components!" << llendl;
mIsRawImageValid = 0;
return;
}
mRawImage->scale(w >> i, h >> i) ;
}
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(false);
mCachedRawImage = mRawImage ;
if(mCachedRawImage.notNull())
mCachedRawImage->setInCache(true);
mRawDiscardLevel += i ;
mCachedRawDiscardLevel = mRawDiscardLevel ;
}
}
void LLViewerFetchedTexture::checkCachedRawSculptImage()
{
if(mCachedRawImageReady && mCachedRawDiscardLevel > 0)
{
if(getDiscardLevel() != 0)
{
mCachedRawImageReady = FALSE ;
}
else if(isForSculptOnly())
{
resetTextureStats() ; //do not update this image any more.
}
}
}
void LLViewerFetchedTexture::saveRawImage()
{
if(mRawImage.isNull() || mRawImage == mSavedRawImage || (mSavedRawDiscardLevel >= 0 && mSavedRawDiscardLevel <= mRawDiscardLevel))
{
return ;
}
// This shouldn't happen, but it did on Snowglobe 1.5. Better safe than sorry?
if (!mRawImage->getData())
{
llwarns << "mRawImage->getData() returns NULL" << llendl;
return;
}
mSavedRawDiscardLevel = mRawDiscardLevel ;
mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents()) ;
if(mForceToSaveRawImage && mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel)
{
mForceToSaveRawImage = FALSE ;
}
mLastReferencedSavedRawImageTime = sCurrentTime ;
}
void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard)
{
if(!mForceToSaveRawImage || mDesiredSavedRawDiscardLevel < 0 || mDesiredSavedRawDiscardLevel > desired_discard)
{
mForceToSaveRawImage = TRUE ;
mDesiredSavedRawDiscardLevel = desired_discard ;
//copy from the cached raw image if exists.
if(mCachedRawImage.notNull() && mRawImage.isNull() )
{
mRawImage = mCachedRawImage ;
mRawDiscardLevel = mCachedRawDiscardLevel ;
saveRawImage() ;
mRawImage = NULL ;
mRawDiscardLevel = INVALID_DISCARD_LEVEL ;
}
}
}
void LLViewerFetchedTexture::destroySavedRawImage()
{
clearCallbackEntryList() ;
mSavedRawImage = NULL ;
mForceToSaveRawImage = FALSE ;
mSaveRawImage = FALSE ;
mSavedRawDiscardLevel = -1 ;
mDesiredSavedRawDiscardLevel = -1 ;
mLastReferencedSavedRawImageTime = 0.0f ;
}
LLImageRaw* LLViewerFetchedTexture::getSavedRawImage()
{
mLastReferencedSavedRawImageTime = sCurrentTime ;
return mSavedRawImage ;
}
BOOL LLViewerFetchedTexture::hasSavedRawImage() const
{
return mSavedRawImage.notNull() ;
}
F32 LLViewerFetchedTexture::getElapsedLastReferencedSavedRawImageTime() const
{
return sCurrentTime - mLastReferencedSavedRawImageTime ;
}
//----------------------------------------------------------------------------------------------
//atlasing
//----------------------------------------------------------------------------------------------
/*void LLViewerFetchedTexture::resetFaceAtlas()
{
//Nothing should be done here.
}
//invalidate all atlas slots for this image.
void LLViewerFetchedTexture::invalidateAtlas(BOOL rebuild_geom)
{
for(U32 i = 0 ; i < mNumFaces ; i++)
{
LLFace* facep = mFaceList[i] ;
facep->removeAtlas() ;
if(rebuild_geom && facep->getDrawable() && facep->getDrawable()->getSpatialGroup())
{
facep->getDrawable()->getSpatialGroup()->setState(LLSpatialGroup::GEOM_DIRTY);
}
}
}
BOOL LLViewerFetchedTexture::insertToAtlas()
{
if(!LLViewerTexture::sUseTextureAtlas)
{
return FALSE ;
}
if(getNumFaces() < 1)
{
return FALSE ;
}
if(mGLTexturep->getDiscardLevelInAtlas() > 0 && mRawDiscardLevel >= mGLTexturep->getDiscardLevelInAtlas())
{
return FALSE ;
}
if(!LLTextureAtlasManager::getInstance()->canAddToAtlas(mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents(), mGLTexturep->getTexTarget()))
{
return FALSE ;
}
BOOL ret = TRUE ;//if ret is set to false, will generate a gl texture for this image.
S32 raw_w = mRawImage->getWidth() ;
S32 raw_h = mRawImage->getHeight() ;
F32 xscale = 1.0f, yscale = 1.0f ;
LLPointer<LLTextureAtlasSlot> slot_infop;
LLTextureAtlasSlot* cur_slotp ;//no need to be smart pointer.
LLSpatialGroup* groupp ;
LLFace* facep;
//if the atlas slot pointers for some faces are null, process them later.
ll_face_list_t waiting_list ;
for(U32 i = 0 ; i < mNumFaces ; i++)
{
{
facep = mFaceList[i] ;
//face can not use atlas.
if(!facep->canUseAtlas())
{
if(facep->getAtlasInfo())
{
facep->removeAtlas() ;
}
ret = FALSE ;
continue ;
}
//the atlas slot is updated
slot_infop = facep->getAtlasInfo() ;
groupp = facep->getDrawable()->getSpatialGroup() ;
if(slot_infop)
{
if(slot_infop->getSpatialGroup() != groupp)
{
if((cur_slotp = groupp->getCurUpdatingSlot(this))) //switch slot
{
facep->setAtlasInfo(cur_slotp) ;
facep->setAtlasInUse(TRUE) ;
continue ;
}
else //do not forget to update slot_infop->getSpatialGroup().
{
LLSpatialGroup* gp = slot_infop->getSpatialGroup() ;
gp->setCurUpdatingTime(gFrameCount) ;
gp->setCurUpdatingTexture(this) ;
gp->setCurUpdatingSlot(slot_infop) ;
}
}
else //same group
{
if(gFrameCount && slot_infop->getUpdatedTime() == gFrameCount)//slot is just updated
{
facep->setAtlasInUse(TRUE) ;
continue ;
}
}
}
else
{
//if the slot is null, wait to process them later.
waiting_list.push_back(facep) ;
continue ;
}
//----------
//insert to atlas
if(!slot_infop->getAtlas()->insertSubTexture(mGLTexturep, mRawDiscardLevel, mRawImage, slot_infop->getSlotCol(), slot_infop->getSlotRow()))
{
//the texture does not qualify to add to atlas, do not bother to try for other faces.
//invalidateAtlas();
return FALSE ;
}
//update texture scale
slot_infop->getAtlas()->getTexCoordScale(raw_w, raw_h, xscale, yscale) ;
slot_infop->setTexCoordScale(xscale, yscale) ;
slot_infop->setValid() ;
slot_infop->setUpdatedTime(gFrameCount) ;
//update spatial group atlas info
groupp->setCurUpdatingTime(gFrameCount) ;
groupp->setCurUpdatingTexture(this) ;
groupp->setCurUpdatingSlot(slot_infop) ;
//make the face to switch to the atlas.
facep->setAtlasInUse(TRUE) ;
}
}
//process the waiting_list
for(ll_face_list_t::iterator iter = waiting_list.begin(); iter != waiting_list.end(); ++iter)
{
facep = (LLFace*)*iter ;
groupp = facep->getDrawable()->getSpatialGroup() ;
//check if this texture already inserted to atlas for this group
if((cur_slotp = groupp->getCurUpdatingSlot(this)))
{
facep->setAtlasInfo(cur_slotp) ;
facep->setAtlasInUse(TRUE) ;
continue ;
}
//need to reserve a slot from atlas
slot_infop = LLTextureAtlasManager::getInstance()->reserveAtlasSlot(llmax(mFullWidth, mFullHeight), getComponents(), groupp, this) ;
facep->setAtlasInfo(slot_infop) ;
groupp->setCurUpdatingTime(gFrameCount) ;
groupp->setCurUpdatingTexture(this) ;
groupp->setCurUpdatingSlot(slot_infop) ;
//slot allocation failed.
if(!slot_infop || !slot_infop->getAtlas())
{
ret = FALSE ;
facep->setAtlasInUse(FALSE) ;
continue ;
}
//insert to atlas
if(!slot_infop->getAtlas()->insertSubTexture(mGLTexturep, mRawDiscardLevel, mRawImage, slot_infop->getSlotCol(), slot_infop->getSlotRow()))
{
//the texture does not qualify to add to atlas, do not bother to try for other faces.
ret = FALSE ;
//invalidateAtlas();
break ;
}
//update texture scale
slot_infop->getAtlas()->getTexCoordScale(raw_w, raw_h, xscale, yscale) ;
slot_infop->setTexCoordScale(xscale, yscale) ;
slot_infop->setValid() ;
slot_infop->setUpdatedTime(gFrameCount) ;
//make the face to switch to the atlas.
facep->setAtlasInUse(TRUE) ;
}
return ret ;
}*/
//----------------------------------------------------------------------------------------------
//end of LLViewerFetchedTexture
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//start of LLViewerLODTexture
//----------------------------------------------------------------------------------------------
LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, const LLHost& host, BOOL usemipmaps)
: LLViewerFetchedTexture(id, host, usemipmaps)
{
init(TRUE) ;
}
LLViewerLODTexture::LLViewerLODTexture(const std::string& url, const LLUUID& id, BOOL usemipmaps)
: LLViewerFetchedTexture(url, id, usemipmaps)
{
init(TRUE) ;
}
void LLViewerLODTexture::init(bool firstinit)
{
mTexelsPerImage = 64.f*64.f;
mDiscardVirtualSize = 0.f;
mCalculatedDiscardLevel = -1.f;
}
//virtual
S8 LLViewerLODTexture::getType() const
{
return LLViewerTexture::LOD_TEXTURE ;
}
BOOL LLViewerLODTexture::isUpdateFrozen()
{
return LLViewerTexture::sFreezeImageScalingDown && !getDiscardLevel() ;
}
// This is gauranteed to get called periodically for every texture
//virtual
void LLViewerLODTexture::processTextureStats()
{
updateVirtualSize() ;
static LLCachedControl<bool> textures_fullres("TextureLoadFullRes", false);
if (textures_fullres)
{
mDesiredDiscardLevel = 0;
}
// Generate the request priority and render priority
else if (mDontDiscard || !mUseMipMaps)
{
mDesiredDiscardLevel = 0;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
mDesiredDiscardLevel = 1; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
}
else if (mBoostLevel < LLViewerTexture::BOOST_HIGH && mMaxVirtualSize <= 10.f)
{
// If the image has not been significantly visible in a while, we don't want it
mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1));
}
else if (!mFullWidth || !mFullHeight)
{
mDesiredDiscardLevel = getMaxDiscardLevel() ;
}
else
{
//static const F64 log_2 = log(2.0);
static const F64 log_4 = log(4.0);
F32 discard_level = 0.f;
// If we know the output width and height, we can force the discard
// level to the correct value, and thus not decode more texture
// data than we need to.
if (mKnownDrawWidth && mKnownDrawHeight)
{
S32 draw_texels = mKnownDrawWidth * mKnownDrawHeight;
// Use log_4 because we're in square-pixel space, so an image
// with twice the width and twice the height will have mTexelsPerImage
// 4 * draw_size
discard_level = (F32)(log(mTexelsPerImage/draw_texels) / log_4);
}
else
{
if(isLargeImage() && !isJustBound() && mAdditionalDecodePriority < 0.3f)
{
//if is a big image and not being used recently, nor close to the view point, do not load hi-res data.
mMaxVirtualSize = llmin(mMaxVirtualSize, (F32)LLViewerTexture::sMinLargeImageSize) ;
}
if ((mCalculatedDiscardLevel >= 0.f) &&
(llabs(mMaxVirtualSize - mDiscardVirtualSize) < mMaxVirtualSize*.20f))
{
// < 20% change in virtual size = no change in desired discard
discard_level = mCalculatedDiscardLevel;
}
else
{
// Calculate the required scale factor of the image using pixels per texel
discard_level = (F32)(log(mTexelsPerImage/mMaxVirtualSize) / log_4);
mDiscardVirtualSize = mMaxVirtualSize;
mCalculatedDiscardLevel = discard_level;
}
}
if (mBoostLevel < LLViewerTexture::BOOST_SCULPTED)
{
discard_level += sDesiredDiscardBias;
discard_level *= sDesiredDiscardScale; // scale
discard_level += sCameraMovingDiscardBias ;
}
discard_level = floorf(discard_level);
F32 min_discard = 0.f;
if (mFullWidth > MAX_IMAGE_SIZE_DEFAULT || mFullHeight > MAX_IMAGE_SIZE_DEFAULT)
min_discard = 1.f; // MAX_IMAGE_SIZE_DEFAULT = 1024 and max size ever is 2048
discard_level = llclamp(discard_level, min_discard, (F32)MAX_DISCARD_LEVEL);
// Can't go higher than the max discard level
mDesiredDiscardLevel = llmin(getMaxDiscardLevel() + 1, (S32)discard_level);
// Clamp to min desired discard
mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, mDesiredDiscardLevel);
//
// At this point we've calculated the quality level that we want,
// if possible. Now we check to see if we have it, and take the
// proper action if we don't.
//
S32 current_discard = getDiscardLevel();
if (sDesiredDiscardBias > 0.0f && mBoostLevel < LLViewerTexture::BOOST_SCULPTED && current_discard >= 0)
{
// Limit the amount of GL memory bound each frame
if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale &&
(!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
{
scaleDown() ;
}
// Only allow GL to have 2x the video card memory
else if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale &&
(!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
{
scaleDown() ;
}
}
}
if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0)
{
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S8)mDesiredSavedRawDiscardLevel) ;
}
}
void LLViewerLODTexture::scaleDown()
{
if(hasGLTexture() && mCachedRawDiscardLevel > getDiscardLevel())
{
switchToCachedImage() ;
/*LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
if (tester)
{
tester->setStablizingTime() ;
}*/
}
}
//----------------------------------------------------------------------------------------------
//end of LLViewerLODTexture
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//start of LLViewerMediaTexture
//----------------------------------------------------------------------------------------------
#if NEW_MEDIA_TEXTURE
//static
void LLViewerMediaTexture::updateClass()
{
static const F32 MAX_INACTIVE_TIME = 30.f ;
#if NEW_MEDIA_TEXTURE
//force to play media.
gSavedSettings.setBOOL("AudioStreamingMedia", true) ;
#endif
for(media_map_t::iterator iter = sMediaMap.begin() ; iter != sMediaMap.end(); )
{
LLViewerMediaTexture* mediap = iter->second;
if(mediap->getNumRefs() == 1) //one reference by sMediaMap
{
//
//Note: delay some time to delete the media textures to stop endlessly creating and immediately removing media texture.
//
if(mediap->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME)
{
media_map_t::iterator cur = iter++ ;
sMediaMap.erase(cur) ;
continue ;
}
}
++iter ;
}
}
//static
void LLViewerMediaTexture::removeMediaImplFromTexture(const LLUUID& media_id)
{
LLViewerMediaTexture* media_tex = findMediaTexture(media_id) ;
if(media_tex)
{
media_tex->invalidateMediaImpl() ;
}
}
//static
void LLViewerMediaTexture::cleanUpClass()
{
sMediaMap.clear() ;
}
//static
LLViewerMediaTexture* LLViewerMediaTexture::findMediaTexture(const LLUUID& media_id)
{
media_map_t::iterator iter = sMediaMap.find(media_id);
if(iter == sMediaMap.end())
{
return NULL;
}
LLViewerMediaTexture* media_tex = iter->second ;
media_tex->setMediaImpl() ;
media_tex->getLastReferencedTimer()->reset() ;
return media_tex;
}
LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LLImageGL* gl_image)
: LLViewerTexture(id, usemipmaps),
mMediaImplp(NULL),
mUpdateVirtualSizeTime(0)
{
sMediaMap.insert(std::make_pair(id, this));
mGLTexturep = gl_image ;
if(mGLTexturep.isNull())
{
generateGLTexture() ;
}
mGLTexturep->setNeedsAlphaAndPickMask(FALSE) ;
mIsPlaying = FALSE ;
setMediaImpl() ;
setCategory(LLViewerTexture::MEDIA) ;
LLViewerTexture* tex = gTextureList.findImage(mID) ;
if(tex) //this media is a parcel media for tex.
{
tex->setParcelMedia(this) ;
}
}
//virtual
LLViewerMediaTexture::~LLViewerMediaTexture()
{
LLViewerTexture* tex = gTextureList.findImage(mID) ;
if(tex) //this media is a parcel media for tex.
{
tex->setParcelMedia(NULL) ;
}
}
#endif
void LLViewerMediaTexture::reinit(BOOL usemipmaps /* = TRUE */)
{
llassert(mGLTexturep.notNull()) ;
mUseMipMaps = usemipmaps ;
getLastReferencedTimer()->reset() ;
mGLTexturep->setUseMipMaps(mUseMipMaps) ;
mGLTexturep->setNeedsAlphaAndPickMask(FALSE) ;
}
void LLViewerMediaTexture::setUseMipMaps(BOOL mipmap)
{
mUseMipMaps = mipmap;
if(mGLTexturep.notNull())
{
mGLTexturep->setUseMipMaps(mipmap) ;
}
}
#if NEW_MEDIA_TEXTURE
//virtual
S8 LLViewerMediaTexture::getType() const
{
return LLViewerTexture::MEDIA_TEXTURE ;
}
void LLViewerMediaTexture::invalidateMediaImpl()
{
mMediaImplp = NULL ;
}
void LLViewerMediaTexture::setMediaImpl()
{
if(!mMediaImplp)
{
mMediaImplp = LLViewerMedia::getMediaImplFromTextureID(mID) ;
}
}
//return true if all faces to reference to this media texture are found
//Note: mMediaFaceList is valid only for the current instant
// because it does not check the face validity after the current frame.
BOOL LLViewerMediaTexture::findFaces()
{
mMediaFaceList.clear() ;
BOOL ret = TRUE ;
LLViewerTexture* tex = gTextureList.findImage(mID) ;
if(tex) //this media is a parcel media for tex.
{
const ll_face_list_t* face_list = tex->getFaceList() ;
U32 end = tex->getNumFaces() ;
for(U32 i = 0 ; i < end ; i++)
{
mMediaFaceList.push_back((*face_list)[i]) ;
}
}
if(!mMediaImplp)
{
return TRUE ;
}
//for media on a face.
const std::list< LLVOVolume* >* obj_list = mMediaImplp->getObjectList() ;
std::list< LLVOVolume* >::const_iterator iter = obj_list->begin() ;
for(; iter != obj_list->end(); ++iter)
{
LLVOVolume* obj = *iter ;
if(obj->mDrawable.isNull())
{
ret = FALSE ;
continue ;
}
S32 face_id = -1 ;
S32 num_faces = obj->mDrawable->getNumFaces() ;
while((face_id = obj->getFaceIndexWithMediaImpl(mMediaImplp, face_id)) > -1 && face_id < num_faces)
{
LLFace* facep = obj->mDrawable->getFace(face_id) ;
if(facep)
{
mMediaFaceList.push_back(facep) ;
}
else
{
ret = FALSE ;
}
}
}
return ret ;
}
void LLViewerMediaTexture::initVirtualSize()
{
if(mIsPlaying)
{
return ;
}
findFaces() ;
for(std::list< LLFace* >::iterator iter = mMediaFaceList.begin(); iter!= mMediaFaceList.end(); ++iter)
{
addTextureStats((*iter)->getVirtualSize()) ;
}
}
void LLViewerMediaTexture::addMediaToFace(LLFace* facep)
{
if(facep)
{
facep->setHasMedia(true) ;
}
if(!mIsPlaying)
{
return ; //no need to add the face because the media is not in playing.
}
switchTexture(facep) ;
}
void LLViewerMediaTexture::removeMediaFromFace(LLFace* facep)
{
if(!facep)
{
return ;
}
facep->setHasMedia(false) ;
if(!mIsPlaying)
{
return ; //no need to remove the face because the media is not in playing.
}
mIsPlaying = FALSE ; //set to remove the media from the face.
switchTexture(facep) ;
mIsPlaying = TRUE ; //set the flag back.
if(getNumFaces() < 1) //no face referencing to this media
{
stopPlaying() ;
}
}
//virtual
void LLViewerMediaTexture::addFace(LLFace* facep)
{
LLViewerTexture::addFace(facep) ;
const LLTextureEntry* te = facep->getTextureEntry() ;
if(te && te->getID().notNull())
{
LLViewerTexture* tex = gTextureList.findImage(te->getID()) ;
if(tex)
{
mTextureList.push_back(tex) ;//increase the reference number by one for tex to avoid deleting it.
return ;
}
}
//check if it is a parcel media
if(facep->getTexture() && facep->getTexture() != this && facep->getTexture()->getID() == mID)
{
mTextureList.push_back(facep->getTexture()) ; //a parcel media.
return ;
}
if(te && te->getID().notNull()) //should have a texture
{
llerrs << "The face does not have a valid texture before media texture." << llendl ;
}
}
//virtual
void LLViewerMediaTexture::removeFace(LLFace* facep)
{
LLViewerTexture::removeFace(facep) ;
const LLTextureEntry* te = facep->getTextureEntry() ;
if(te && te->getID().notNull())
{
LLViewerTexture* tex = gTextureList.findImage(te->getID()) ;
if(tex)
{
for(std::list< LLPointer<LLViewerTexture> >::iterator iter = mTextureList.begin();
iter != mTextureList.end(); ++iter)
{
if(*iter == tex)
{
mTextureList.erase(iter) ; //decrease the reference number for tex by one.
return ;
}
}
//
//we have some trouble here: the texture of the face is changed.
//we need to find the former texture, and remove it from the list to avoid memory leaking.
if(!mNumFaces)
{
mTextureList.clear() ;
return ;
}
S32 end = getNumFaces() ;
std::vector<const LLTextureEntry*> te_list(end) ;
S32 i = 0 ;
for(U32 j = 0 ; j < mNumFaces ; j++)
{
te_list[i++] = mFaceList[j]->getTextureEntry() ;//all textures are in use.
}
for(std::list< LLPointer<LLViewerTexture> >::iterator iter = mTextureList.begin();
iter != mTextureList.end(); ++iter)
{
for(i = 0 ; i < end ; i++)
{
if(te_list[i] && te_list[i]->getID() == (*iter)->getID())//the texture is in use.
{
te_list[i] = NULL ;
break ;
}
}
if(i == end) //no hit for this texture, remove it.
{
mTextureList.erase(iter) ; //decrease the reference number for tex by one.
return ;
}
}
}
}
//check if it is a parcel media
for(std::list< LLPointer<LLViewerTexture> >::iterator iter = mTextureList.begin();
iter != mTextureList.end(); ++iter)
{
if((*iter)->getID() == mID)
{
mTextureList.erase(iter) ; //decrease the reference number for tex by one.
return ;
}
}
if(te && te->getID().notNull()) //should have a texture
{
llerrs << "mTextureList texture reference number is corrupted." << llendl ;
}
}
void LLViewerMediaTexture::stopPlaying()
{
// Don't stop the media impl playing here -- this breaks non-inworld media (login screen, search, and media browser).
// if(mMediaImplp)
// {
// mMediaImplp->stop() ;
// }
mIsPlaying = FALSE ;
}
void LLViewerMediaTexture::switchTexture(LLFace* facep)
{
if(facep)
{
//check if another media is playing on this face.
if(facep->getTexture() && facep->getTexture() != this
&& facep->getTexture()->getType() == LLViewerTexture::MEDIA_TEXTURE)
{
if(mID == facep->getTexture()->getID()) //this is a parcel media
{
return ; //let the prim media win.
}
}
if(mIsPlaying) //old textures switch to the media texture
{
facep->switchTexture(this) ;
}
else //switch to old textures.
{
const LLTextureEntry* te = facep->getTextureEntry() ;
if(te)
{
LLViewerTexture* tex = te->getID().notNull() ? gTextureList.findImage(te->getID()) : NULL ;
if(!tex && te->getID() != mID)//try parcel media.
{
tex = gTextureList.findImage(mID) ;
}
if(!tex)
{
tex = LLViewerFetchedTexture::sDefaultImagep ;
}
facep->switchTexture(tex) ;
}
}
}
}
void LLViewerMediaTexture::setPlaying(BOOL playing)
{
if(!mMediaImplp)
{
return ;
}
if(!playing && !mIsPlaying)
{
return ; //media is already off
}
if(playing == mIsPlaying && !mMediaImplp->isUpdated())
{
return ; //nothing has changed since last time.
}
mIsPlaying = playing ;
if(mIsPlaying) //is about to play this media
{
if(findFaces())
{
//about to update all faces.
mMediaImplp->setUpdated(FALSE) ;
}
if(mMediaFaceList.empty())//no face pointing to this media
{
stopPlaying() ;
return ;
}
for(std::list< LLFace* >::iterator iter = mMediaFaceList.begin(); iter!= mMediaFaceList.end(); ++iter)
{
switchTexture(*iter) ;
}
}
else //stop playing this media
{
for(U32 i = mNumFaces ; i ; i--)
{
switchTexture(mFaceList[i - 1]) ; //current face could be removed in this function.
}
}
return ;
}
//virtual
F32 LLViewerMediaTexture::getMaxVirtualSize()
{
if(LLFrameTimer::getFrameCount() == mUpdateVirtualSizeTime)
{
return mMaxVirtualSize ;
}
mUpdateVirtualSizeTime = LLFrameTimer::getFrameCount() ;
if(!mMaxVirtualSizeResetCounter)
{
addTextureStats(0.f, FALSE) ;//reset
}
if(mIsPlaying) //media is playing
{
for(U32 i = 0 ; i < mNumFaces ; i++)
{
LLFace* facep = mFaceList[i] ;
if(facep->getDrawable()->isRecentlyVisible())
{
addTextureStats(facep->getVirtualSize()) ;
}
}
}
else //media is not in playing
{
findFaces() ;
if(!mMediaFaceList.empty())
{
for(std::list< LLFace* >::iterator iter = mMediaFaceList.begin(); iter!= mMediaFaceList.end(); ++iter)
{
LLFace* facep = *iter ;
if(facep->getDrawable()->isRecentlyVisible())
{
addTextureStats(facep->getVirtualSize()) ;
}
}
}
}
if(mMaxVirtualSizeResetCounter > 0)
{
mMaxVirtualSizeResetCounter--;
}
reorganizeFaceList() ;
reorganizeVolumeList();
return mMaxVirtualSize ;
}
#endif
//----------------------------------------------------------------------------------------------
//end of LLViewerMediaTexture
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
//start of LLTexturePipelineTester
//----------------------------------------------------------------------------------------------
#if 0
LLTexturePipelineTester::LLTexturePipelineTester() : LLMetricPerformanceTesterWithSession(sTesterName)
{
addMetric("TotalBytesLoaded") ;
addMetric("TotalBytesLoadedFromCache") ;
addMetric("TotalBytesLoadedForLargeImage") ;
addMetric("TotalBytesLoadedForSculpties") ;
addMetric("StartFetchingTime") ;
addMetric("TotalGrayTime") ;
addMetric("TotalStablizingTime") ;
addMetric("StartTimeLoadingSculpties") ;
addMetric("EndTimeLoadingSculpties") ;
addMetric("Time") ;
addMetric("TotalBytesBound") ;
addMetric("TotalBytesBoundForLargeImage") ;
addMetric("PercentageBytesBound") ;
mTotalBytesLoaded = 0 ;
mTotalBytesLoadedFromCache = 0 ;
mTotalBytesLoadedForLargeImage = 0 ;
mTotalBytesLoadedForSculpties = 0 ;
reset() ;
}
LLTexturePipelineTester::~LLTexturePipelineTester()
{
LLViewerTextureManager::sTesterp = NULL ;
}
void LLTexturePipelineTester::update()
{
mLastTotalBytesUsed = mTotalBytesUsed ;
mLastTotalBytesUsedForLargeImage = mTotalBytesUsedForLargeImage ;
mTotalBytesUsed = 0 ;
mTotalBytesUsedForLargeImage = 0 ;
if(LLAppViewer::getTextureFetch()->getNumRequests() > 0) //fetching list is not empty
{
if(mPause)
{
//start a new fetching session
reset() ;
mStartFetchingTime = LLImageGL::sLastFrameTime ;
mPause = FALSE ;
}
//update total gray time
if(mUsingDefaultTexture)
{
mUsingDefaultTexture = FALSE ;
mTotalGrayTime = LLImageGL::sLastFrameTime - mStartFetchingTime ;
}
//update the stablizing timer.
updateStablizingTime() ;
outputTestResults() ;
}
else if(!mPause)
{
//stop the current fetching session
mPause = TRUE ;
outputTestResults() ;
reset() ;
}
}
void LLTexturePipelineTester::reset()
{
mPause = TRUE ;
mUsingDefaultTexture = FALSE ;
mStartStablizingTime = 0.0f ;
mEndStablizingTime = 0.0f ;
mTotalBytesUsed = 0 ;
mTotalBytesUsedForLargeImage = 0 ;
mLastTotalBytesUsed = 0 ;
mLastTotalBytesUsedForLargeImage = 0 ;
mStartFetchingTime = 0.0f ;
mTotalGrayTime = 0.0f ;
mTotalStablizingTime = 0.0f ;
mStartTimeLoadingSculpties = 1.0f ;
mEndTimeLoadingSculpties = 0.0f ;
}
//virtual
void LLTexturePipelineTester::outputTestRecord(LLSD *sd)
{
(*sd)[mCurLabel]["TotalBytesLoaded"] = (LLSD::Integer)mTotalBytesLoaded ;
(*sd)[mCurLabel]["TotalBytesLoadedFromCache"] = (LLSD::Integer)mTotalBytesLoadedFromCache ;
(*sd)[mCurLabel]["TotalBytesLoadedForLargeImage"] = (LLSD::Integer)mTotalBytesLoadedForLargeImage ;
(*sd)[mCurLabel]["TotalBytesLoadedForSculpties"] = (LLSD::Integer)mTotalBytesLoadedForSculpties ;
(*sd)[mCurLabel]["StartFetchingTime"] = (LLSD::Real)mStartFetchingTime ;
(*sd)[mCurLabel]["TotalGrayTime"] = (LLSD::Real)mTotalGrayTime ;
(*sd)[mCurLabel]["TotalStablizingTime"] = (LLSD::Real)mTotalStablizingTime ;
(*sd)[mCurLabel]["StartTimeLoadingSculpties"] = (LLSD::Real)mStartTimeLoadingSculpties ;
(*sd)[mCurLabel]["EndTimeLoadingSculpties"] = (LLSD::Real)mEndTimeLoadingSculpties ;
(*sd)[mCurLabel]["Time"] = LLImageGL::sLastFrameTime ;
(*sd)[mCurLabel]["TotalBytesBound"] = (LLSD::Integer)mLastTotalBytesUsed ;
(*sd)[mCurLabel]["TotalBytesBoundForLargeImage"] = (LLSD::Integer)mLastTotalBytesUsedForLargeImage ;
(*sd)[mCurLabel]["PercentageBytesBound"] = (LLSD::Real)(100.f * mLastTotalBytesUsed / mTotalBytesLoaded) ;
}
void LLTexturePipelineTester::updateTextureBindingStats(const LLViewerTexture* imagep)
{
U32 mem_size = (U32)imagep->getTextureMemory() ;
mTotalBytesUsed += mem_size ;
if(MIN_LARGE_IMAGE_AREA <= (U32)(mem_size / (U32)imagep->getComponents()))
{
mTotalBytesUsedForLargeImage += mem_size ;
}
}
void LLTexturePipelineTester::updateTextureLoadingStats(const LLViewerFetchedTexture* imagep, const LLImageRaw* raw_imagep, BOOL from_cache)
{
U32 data_size = (U32)raw_imagep->getDataSize() ;
mTotalBytesLoaded += data_size ;
if(from_cache)
{
mTotalBytesLoadedFromCache += data_size ;
}
if(MIN_LARGE_IMAGE_AREA <= (U32)(data_size / (U32)raw_imagep->getComponents()))
{
mTotalBytesLoadedForLargeImage += data_size ;
}
if(imagep->forSculpt())
{
mTotalBytesLoadedForSculpties += data_size ;
if(mStartTimeLoadingSculpties > mEndTimeLoadingSculpties)
{
mStartTimeLoadingSculpties = LLImageGL::sLastFrameTime ;
}
mEndTimeLoadingSculpties = LLImageGL::sLastFrameTime ;
}
}
void LLTexturePipelineTester::updateGrayTextureBinding()
{
mUsingDefaultTexture = TRUE ;
}
void LLTexturePipelineTester::setStablizingTime()
{
if(mStartStablizingTime <= mStartFetchingTime)
{
mStartStablizingTime = LLImageGL::sLastFrameTime ;
}
mEndStablizingTime = LLImageGL::sLastFrameTime ;
}
void LLTexturePipelineTester::updateStablizingTime()
{
if(mStartStablizingTime > mStartFetchingTime)
{
F32 t = mEndStablizingTime - mStartStablizingTime ;
if(t > F_ALMOST_ZERO && (t - mTotalStablizingTime) < F_ALMOST_ZERO)
{
//already stablized
mTotalStablizingTime = LLImageGL::sLastFrameTime - mStartStablizingTime ;
//cancel the timer
mStartStablizingTime = 0.f ;
mEndStablizingTime = 0.f ;
}
else
{
mTotalStablizingTime = t ;
}
}
mTotalStablizingTime = 0.f ;
}
//virtual
void LLTexturePipelineTester::compareTestSessions(std::ofstream* os)
{
LLTexturePipelineTester::LLTextureTestSession* base_sessionp = dynamic_cast<LLTexturePipelineTester::LLTextureTestSession*>(mBaseSessionp) ;
LLTexturePipelineTester::LLTextureTestSession* current_sessionp = dynamic_cast<LLTexturePipelineTester::LLTextureTestSession*>(mCurrentSessionp) ;
if(!base_sessionp || !current_sessionp)
{
llerrs << "type of test session does not match!" << llendl ;
}
//compare and output the comparison
*os << llformat("%s\n", mName.c_str()) ;
*os << llformat("AggregateResults\n") ;
compareTestResults(os, "TotalFetchingTime", base_sessionp->mTotalFetchingTime, current_sessionp->mTotalFetchingTime) ;
compareTestResults(os, "TotalGrayTime", base_sessionp->mTotalGrayTime, current_sessionp->mTotalGrayTime) ;
compareTestResults(os, "TotalStablizingTime", base_sessionp->mTotalStablizingTime, current_sessionp->mTotalStablizingTime);
compareTestResults(os, "StartTimeLoadingSculpties", base_sessionp->mStartTimeLoadingSculpties, current_sessionp->mStartTimeLoadingSculpties) ;
compareTestResults(os, "TotalTimeLoadingSculpties", base_sessionp->mTotalTimeLoadingSculpties, current_sessionp->mTotalTimeLoadingSculpties) ;
compareTestResults(os, "TotalBytesLoaded", base_sessionp->mTotalBytesLoaded, current_sessionp->mTotalBytesLoaded) ;
compareTestResults(os, "TotalBytesLoadedFromCache", base_sessionp->mTotalBytesLoadedFromCache, current_sessionp->mTotalBytesLoadedFromCache) ;
compareTestResults(os, "TotalBytesLoadedForLargeImage", base_sessionp->mTotalBytesLoadedForLargeImage, current_sessionp->mTotalBytesLoadedForLargeImage) ;
compareTestResults(os, "TotalBytesLoadedForSculpties", base_sessionp->mTotalBytesLoadedForSculpties, current_sessionp->mTotalBytesLoadedForSculpties) ;
*os << llformat("InstantResults\n") ;
S32 size = llmin(base_sessionp->mInstantPerformanceListCounter, current_sessionp->mInstantPerformanceListCounter) ;
for(S32 i = 0 ; i < size ; i++)
{
*os << llformat("Time(B-T)-%.4f-%.4f\n", base_sessionp->mInstantPerformanceList[i].mTime, current_sessionp->mInstantPerformanceList[i].mTime) ;
compareTestResults(os, "AverageBytesUsedPerSecond", base_sessionp->mInstantPerformanceList[i].mAverageBytesUsedPerSecond,
current_sessionp->mInstantPerformanceList[i].mAverageBytesUsedPerSecond) ;
compareTestResults(os, "AverageBytesUsedForLargeImagePerSecond", base_sessionp->mInstantPerformanceList[i].mAverageBytesUsedForLargeImagePerSecond,
current_sessionp->mInstantPerformanceList[i].mAverageBytesUsedForLargeImagePerSecond) ;
compareTestResults(os, "AveragePercentageBytesUsedPerSecond", base_sessionp->mInstantPerformanceList[i].mAveragePercentageBytesUsedPerSecond,
current_sessionp->mInstantPerformanceList[i].mAveragePercentageBytesUsedPerSecond) ;
}
if(size < base_sessionp->mInstantPerformanceListCounter)
{
for(S32 i = size ; i < base_sessionp->mInstantPerformanceListCounter ; i++)
{
*os << llformat("Time(B-T)-%.4f- \n", base_sessionp->mInstantPerformanceList[i].mTime) ;
*os << llformat(", AverageBytesUsedPerSecond, %d, N/A \n", base_sessionp->mInstantPerformanceList[i].mAverageBytesUsedPerSecond) ;
*os << llformat(", AverageBytesUsedForLargeImagePerSecond, %d, N/A \n", base_sessionp->mInstantPerformanceList[i].mAverageBytesUsedForLargeImagePerSecond) ;
*os << llformat(", AveragePercentageBytesUsedPerSecond, %.4f, N/A \n", base_sessionp->mInstantPerformanceList[i].mAveragePercentageBytesUsedPerSecond) ;
}
}
else if(size < current_sessionp->mInstantPerformanceListCounter)
{
for(S32 i = size ; i < current_sessionp->mInstantPerformanceListCounter ; i++)
{
*os << llformat("Time(B-T)- -%.4f\n", current_sessionp->mInstantPerformanceList[i].mTime) ;
*os << llformat(", AverageBytesUsedPerSecond, N/A, %d\n", current_sessionp->mInstantPerformanceList[i].mAverageBytesUsedPerSecond) ;
*os << llformat(", AverageBytesUsedForLargeImagePerSecond, N/A, %d\n", current_sessionp->mInstantPerformanceList[i].mAverageBytesUsedForLargeImagePerSecond) ;
*os << llformat(", AveragePercentageBytesUsedPerSecond, N/A, %.4f\n", current_sessionp->mInstantPerformanceList[i].mAveragePercentageBytesUsedPerSecond) ;
}
}
}
//virtual
LLMetricPerformanceTester::LLTestSession* LLTexturePipelineTester::loadTestSession(LLSD* log)
{
LLTexturePipelineTester::LLTextureTestSession* sessionp = new LLTexturePipelineTester::LLTextureTestSession() ;
if(!sessionp)
{
return NULL ;
}
F32 total_fetching_time = 0.f ;
F32 total_gray_time = 0.f ;
F32 total_stablizing_time = 0.f ;
F32 total_loading_sculpties_time = 0.f ;
F32 start_fetching_time = -1.f ;
F32 start_fetching_sculpties_time = 0.f ;
F32 last_time = 0.0f ;
S32 frame_count = 0 ;
sessionp->mInstantPerformanceListCounter = 0 ;
sessionp->mInstantPerformanceList.resize(128) ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedPerSecond = 0 ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedForLargeImagePerSecond = 0 ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAveragePercentageBytesUsedPerSecond = 0.f ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mTime = 0.f ;
//load a session
BOOL in_log = (*log).has(mCurLabel) ;
while(in_log)
{
LLSD::String label = mCurLabel ;
incLabel() ;
in_log = (*log).has(mCurLabel) ;
if(sessionp->mInstantPerformanceListCounter >= (S32)sessionp->mInstantPerformanceList.size())
{
sessionp->mInstantPerformanceList.resize(sessionp->mInstantPerformanceListCounter + 128) ;
}
//time
F32 start_time = (*log)[label]["StartFetchingTime"].asReal() ;
F32 cur_time = (*log)[label]["Time"].asReal() ;
if(start_time - start_fetching_time > F_ALMOST_ZERO) //fetching has paused for a while
{
sessionp->mTotalFetchingTime += total_fetching_time ;
sessionp->mTotalGrayTime += total_gray_time ;
sessionp->mTotalStablizingTime += total_stablizing_time ;
sessionp->mStartTimeLoadingSculpties = start_fetching_sculpties_time ;
sessionp->mTotalTimeLoadingSculpties += total_loading_sculpties_time ;
start_fetching_time = start_time ;
total_fetching_time = 0.0f ;
total_gray_time = 0.f ;
total_stablizing_time = 0.f ;
total_loading_sculpties_time = 0.f ;
}
else
{
total_fetching_time = cur_time - start_time ;
total_gray_time = (*log)[label]["TotalGrayTime"].asReal() ;
total_stablizing_time = (*log)[label]["TotalStablizingTime"].asReal() ;
total_loading_sculpties_time = (*log)[label]["EndTimeLoadingSculpties"].asReal() - (*log)[label]["StartTimeLoadingSculpties"].asReal() ;
if(start_fetching_sculpties_time < 0.f && total_loading_sculpties_time > 0.f)
{
start_fetching_sculpties_time = (*log)[label]["StartTimeLoadingSculpties"].asReal() ;
}
}
//total loaded bytes
sessionp->mTotalBytesLoaded = (*log)[label]["TotalBytesLoaded"].asInteger() ;
sessionp->mTotalBytesLoadedFromCache = (*log)[label]["TotalBytesLoadedFromCache"].asInteger() ;
sessionp->mTotalBytesLoadedForLargeImage = (*log)[label]["TotalBytesLoadedForLargeImage"].asInteger() ;
sessionp->mTotalBytesLoadedForSculpties = (*log)[label]["TotalBytesLoadedForSculpties"].asInteger() ;
//instant metrics
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedPerSecond +=
(*log)[label]["TotalBytesBound"].asInteger() ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedForLargeImagePerSecond +=
(*log)[label]["TotalBytesBoundForLargeImage"].asInteger() ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAveragePercentageBytesUsedPerSecond +=
(*log)[label]["PercentageBytesBound"].asReal() ;
frame_count++ ;
if(cur_time - last_time >= 1.0f)
{
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedPerSecond /= frame_count ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedForLargeImagePerSecond /= frame_count ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAveragePercentageBytesUsedPerSecond /= frame_count ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mTime = last_time ;
frame_count = 0 ;
last_time = cur_time ;
sessionp->mInstantPerformanceListCounter++ ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedPerSecond = 0 ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAverageBytesUsedForLargeImagePerSecond = 0 ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mAveragePercentageBytesUsedPerSecond = 0.f ;
sessionp->mInstantPerformanceList[sessionp->mInstantPerformanceListCounter].mTime = 0.f ;
}
}
sessionp->mTotalFetchingTime += total_fetching_time ;
sessionp->mTotalGrayTime += total_gray_time ;
sessionp->mTotalStablizingTime += total_stablizing_time ;
if(sessionp->mStartTimeLoadingSculpties < 0.f)
{
sessionp->mStartTimeLoadingSculpties = start_fetching_sculpties_time ;
}
sessionp->mTotalTimeLoadingSculpties += total_loading_sculpties_time ;
return sessionp;
}
LLTexturePipelineTester::LLTextureTestSession::LLTextureTestSession()
{
reset() ;
}
LLTexturePipelineTester::LLTextureTestSession::~LLTextureTestSession()
{
}
void LLTexturePipelineTester::LLTextureTestSession::reset()
{
mTotalFetchingTime = 0.0f ;
mTotalGrayTime = 0.0f ;
mTotalStablizingTime = 0.0f ;
mStartTimeLoadingSculpties = 0.0f ;
mTotalTimeLoadingSculpties = 0.0f ;
mTotalBytesLoaded = 0 ;
mTotalBytesLoadedFromCache = 0 ;
mTotalBytesLoadedForLargeImage = 0 ;
mTotalBytesLoadedForSculpties = 0 ;
mInstantPerformanceListCounter = 0 ;
}
#endif //0
//----------------------------------------------------------------------------------------------
//end of LLTexturePipelineTester
//----------------------------------------------------------------------------------------------