diff options
author | Bryan O'Sullivan <bos@lindenlab.com> | 2009-08-31 13:47:47 -0700 |
---|---|---|
committer | Bryan O'Sullivan <bos@lindenlab.com> | 2009-08-31 13:47:47 -0700 |
commit | 30ff6cabd61f2f083df5df1e6e70cc94742af477 (patch) | |
tree | 631935f1cf59d19a91cdad65e9a75fe825afda7d /indra/newview/llviewertexture.cpp | |
parent | ff11d74820c89822cd067b51727d9df1dc87d0d0 (diff) | |
parent | 3ac3a4b206c08ed06b889bdaa24074b6aa0e020a (diff) |
Merge with trunk
Diffstat (limited to 'indra/newview/llviewertexture.cpp')
-rw-r--r-- | indra/newview/llviewertexture.cpp | 2386 |
1 files changed, 2386 insertions, 0 deletions
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp new file mode 100644 index 0000000000..6ea1522b47 --- /dev/null +++ b/indra/newview/llviewertexture.cpp @@ -0,0 +1,2386 @@ +/** + * @file llviewertexture.cpp + * @brief Object which handles a received image (and associated texture(s)) + * + * $LicenseInfo:firstyear=2000&license=viewergpl$ + * + * Copyright (c) 2000-2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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" +/////////////////////////////////////////////////////////////////////////////// + +// 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; +LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap ; +LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL ; + +S32 LLViewerTexture::sImageCount = 0; +S32 LLViewerTexture::sRawCount = 0; +S32 LLViewerTexture::sAuxCount = 0; +LLTimer 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 ; +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 = 1.5f; // max number of levels to reduce image quality by + +//---------------------------------------------------------------------------------------------- +//namespace: LLViewerTextureAccess +//---------------------------------------------------------------------------------------------- +LLViewerMediaTexture* LLViewerTextureManager::createMediaTexture(const LLUUID &media_id, BOOL usemipmaps, LLImageGL* gl_image) +{ + return new LLViewerMediaTexture(media_id, usemipmaps, gl_image) ; +} + +LLViewerTexture* LLViewerTextureManager::findTexture(const LLUUID& id) +{ + LLViewerTexture* tex ; + //search fetched texture list + tex = gTextureList.findImage(id) ; + + //search media texture list + if(!tex) + { + tex = LLViewerTextureManager::findMediaTexture(id) ; + } + return tex ; +} + +LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id) +{ + LLViewerMediaTexture::media_map_t::iterator iter = LLViewerMediaTexture::sMediaMap.find(media_id); + if(iter == LLViewerMediaTexture::sMediaMap.end()) + return NULL; + + ((LLViewerMediaTexture*)(iter->second))->getLastReferencedTimer()->reset() ; + return iter->second; +} + +LLViewerMediaTexture* LLViewerTextureManager::getMediaTexture(const LLUUID& id, BOOL usemipmaps, LLImageGL* gl_image) +{ + LLViewerMediaTexture* tex = LLViewerTextureManager::findMediaTexture(id) ; + if(!tex) + { + tex = LLViewerTextureManager::createMediaTexture(id, usemipmaps, gl_image) ; + } + + LLViewerTexture* old_tex = tex->getOldTexture() ; + if(!old_tex) + { + //if there is a fetched texture with the same id, replace it by this media texture + old_tex = gTextureList.findImage(id) ; + if(old_tex) + { + tex->setOldTexture(old_tex) ; + } + } + + if (gSavedSettings.getBOOL("ParcelMediaAutoPlayEnable") && gSavedSettings.getBOOL("AudioStreamingVideo")) + { + if(!tex->isPlaying()) + { + if(old_tex) + { + old_tex->switchToTexture(tex) ; + } + tex->setPlaying(TRUE) ; + } + } + tex->getLastReferencedTimer()->reset() ; + + return tex ; +} + +LLViewerFetchedTexture* LLViewerTextureManager::staticCastToFetchedTexture(LLViewerTexture* 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() ; + } + 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() ; + } + return tex ; +} +LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps) +{ + LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps) ; + 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() ; + } + return tex ; +} + +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture( + const LLUUID &image_id, + BOOL usemipmaps, + BOOL level_immediate, + S8 texture_type, + LLGLint internal_format, + LLGLenum primary_format, + LLHost request_from_host) +{ + return gTextureList.getImage(image_id, usemipmaps, level_immediate, texture_type, internal_format, primary_format, request_from_host) ; +} + +LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile( + const std::string& filename, + BOOL usemipmaps, + BOOL level_immediate, + S8 texture_type, + LLGLint internal_format, + LLGLenum primary_format, + const LLUUID& force_id) +{ + return gTextureList.getImageFromFile(filename, usemipmaps, level_immediate, 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 = new LLViewerFetchedTexture(IMG_DEFAULT, TRUE); + 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); + image_raw = NULL; + gTextureList.addImage(imagep); + LLViewerFetchedTexture::sDefaultImagep->dontDiscard(); +#else + LLViewerFetchedTexture::sDefaultImagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT, TRUE, TRUE); +#endif + + LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, TRUE, TRUE); + LLViewerFetchedTexture::sSmokeImagep->setNoDelete() ; + + LLViewerTexture::initClass() ; + + if(LLFastTimer::sMetricLog) + { + LLViewerTextureManager::sTesterp = new LLTexturePipelineTester() ; + } +} + +void LLViewerTextureManager::cleanup() +{ + stop_glerror(); + + LLImageGL::sDefaultGLTexture = NULL ; + LLViewerTexture::sNullImagep = NULL; + LLViewerFetchedTexture::sDefaultImagep = NULL; + LLViewerFetchedTexture::sSmokeImagep = NULL; + LLViewerFetchedTexture::sMissingAssetImagep = NULL; + LLViewerFetchedTexture::sWhiteImagep = NULL; + + LLViewerMediaTexture::sMediaMap.clear() ; +} + +//---------------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------------- +//start of LLViewerTexture +//---------------------------------------------------------------------------------------------- +// static +void LLViewerTexture::initClass() +{ + LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture() ; +} + +// static +void LLViewerTexture::cleanupClass() +{ +} + +// tuning params +const F32 discard_bias_delta = .05f; +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) +{ + if(LLViewerTextureManager::sTesterp) + { + LLViewerTextureManager::sTesterp->update() ; + } + LLViewerMediaTexture::updateClass() ; + + 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); +} + +//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 ; + + 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() +{ + sImageCount--; +} + +void LLViewerTexture::init(bool firstinit) +{ + mBoostLevel = LLViewerTexture::BOOST_NONE; + + mFullWidth = 0; + mFullHeight = 0; + mUseMipMaps = FALSE ; + mComponents = 0 ; + + mTextureState = NO_DELETE ; + mDontDiscard = FALSE; + mMaxVirtualSize = 0.f; +} + +//virtual +S8 LLViewerTexture::getType() const +{ + return LLViewerTexture::LOCAL_TEXTURE ; +} + +void LLViewerTexture::cleanup() +{ + mFaceList.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() ; + } + } +} + + +bool LLViewerTexture::bindDefaultImage(S32 stage) const +{ + 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(); + if(LLViewerTextureManager::sTesterp) + { + LLViewerTextureManager::sTesterp->updateGrayTextureBinding() ; + } + return res; +} + +//virtual +BOOL LLViewerTexture::isMissingAsset()const +{ + return FALSE; +} + +//virtual +void LLViewerTexture::forceImmediateUpdate() +{ +} + +void LLViewerTexture::addTextureStats(F32 virtual_size) const +{ + if (virtual_size > mMaxVirtualSize) + { + mMaxVirtualSize = virtual_size; + } +} + +void LLViewerTexture::resetTextureStats(BOOL zero) +{ + if (zero) + { + mMaxVirtualSize = 0.0f; + } + else + { + mMaxVirtualSize -= mMaxVirtualSize * .10f; // decay by 5%/update + } +} + +void LLViewerTexture::addFace(LLFace* facep) +{ + mFaceList.push_back(facep) ; +} +void LLViewerTexture::removeFace(LLFace* facep) +{ + mFaceList.remove(facep) ; +} + +void LLViewerTexture::switchToTexture(LLViewerTexture* new_texture) +{ + if(this == new_texture) + { + return ; + } + + new_texture->addTextureStats(getMaxVirtualSize()) ; + + for(ll_face_list_t::iterator iter = mFaceList.begin(); iter != mFaceList.end(); ) + { + LLFace* facep = *iter++ ; + facep->setTexture(new_texture) ; + facep->getViewerObject()->changeTEImage(this, new_texture) ; + gPipeline.markTextured(facep->getDrawable()); + } +} + +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_always(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) +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->createGLTexture(discard_level, imageraw, usename) ; +} + +void LLViewerTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes) +{ + llassert_always(mGLTexturep.notNull()) ; + + mGLTexturep->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes) ; +} +void LLViewerTexture::setAddressMode(LLTexUnit::eTextureAddressMode mode) +{ + llassert_always(mGLTexturep.notNull()) ; + mGLTexturep->setAddressMode(mode) ; +} +void LLViewerTexture::setFilteringOption(LLTexUnit::eTextureFilterOptions option) +{ + llassert_always(mGLTexturep.notNull()) ; + mGLTexturep->setFilteringOption(option) ; +} + +//virtual +S32 LLViewerTexture::getWidth(S32 discard_level) const +{ + llassert_always(mGLTexturep.notNull()) ; + return mGLTexturep->getWidth(discard_level) ; +} + +//virtual +S32 LLViewerTexture::getHeight(S32 discard_level) const +{ + llassert_always(mGLTexturep.notNull()) ; + return mGLTexturep->getHeight(discard_level) ; +} + +S32 LLViewerTexture::getMaxDiscardLevel() const +{ + llassert_always(mGLTexturep.notNull()) ; + return mGLTexturep->getMaxDiscardLevel() ; +} +S32 LLViewerTexture::getDiscardLevel() const +{ + llassert_always(mGLTexturep.notNull()) ; + return mGLTexturep->getDiscardLevel() ; +} +S8 LLViewerTexture::getComponents() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getComponents() ; +} + +LLGLuint LLViewerTexture::getTexName() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getTexName() ; +} + +BOOL LLViewerTexture::hasValidGLTexture() const +{ + if(mGLTexturep.notNull()) + { + return mGLTexturep->getHasGLTexture() ; + } + return FALSE ; +} + +BOOL LLViewerTexture::hasGLTexture() const +{ + return mGLTexturep.notNull() ; +} + +BOOL LLViewerTexture::getBoundRecently() const +{ + if(mGLTexturep.notNull()) + { + return mGLTexturep->getBoundRecently() ; + } + return FALSE ; +} + +LLTexUnit::eTextureType LLViewerTexture::getTarget(void) const +{ + llassert_always(mGLTexturep.notNull()) ; + return mGLTexturep->getTarget() ; +} + +BOOL LLViewerTexture::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height) +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->setSubImage(imageraw, x_pos, y_pos, width, height) ; +} + +BOOL LLViewerTexture::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height) +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->setSubImage(datap, data_width, data_height, x_pos, y_pos, width, height) ; +} + +void LLViewerTexture::setGLTextureCreated (bool initialized) +{ + llassert_always(mGLTexturep.notNull()) ; + + mGLTexturep->setGLTextureCreated (initialized) ; +} + +LLTexUnit::eTextureAddressMode LLViewerTexture::getAddressMode(void) const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getAddressMode() ; +} + +S32 LLViewerTexture::getTextureMemory() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->mTextureMemory ; +} + +LLGLenum LLViewerTexture::getPrimaryFormat() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getPrimaryFormat() ; +} + +BOOL LLViewerTexture::getIsAlphaMask() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getIsAlphaMask() ; +} + +BOOL LLViewerTexture::getMask(const LLVector2 &tc) +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getMask(tc) ; +} + +F32 LLViewerTexture::getTimePassedSinceLastBound() +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getTimePassedSinceLastBound() ; +} +BOOL LLViewerTexture::getMissed() const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->getMissed() ; +} + +BOOL LLViewerTexture::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents) +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->isValidForSculpt(discard_level, image_width, image_height, ncomponents) ; +} + +BOOL LLViewerTexture::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const +{ + llassert_always(mGLTexturep.notNull()) ; + + return mGLTexturep->readBackRaw(discard_level, imageraw, compressed_ok) ; +} + +void LLViewerTexture::destroyGLTexture() +{ + if(mGLTexturep.notNull() && mGLTexturep->getHasGLTexture()) + { + mGLTexturep->destroyGLTexture() ; + mTextureState = DELETED ; + } +} + +//virtual +void LLViewerTexture::updateBindStatsForTester() +{ + if(LLViewerTextureManager::sTesterp) + { + LLViewerTextureManager::sTesterp->updateTextureBindingStats(this) ; + } +} +//---------------------------------------------------------------------------------------------- +//end of LLViewerTexture +//---------------------------------------------------------------------------------------------- + +//---------------------------------------------------------------------------------------------- +//start of LLViewerFetchedTexture +//---------------------------------------------------------------------------------------------- + +//static +F32 LLViewerFetchedTexture::maxDecodePriority() +{ + return 2000000.f; +} + +LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, BOOL usemipmaps) + : LLViewerTexture(id, usemipmaps) +{ + init(TRUE) ; + generateGLTexture() ; +} + +LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, BOOL usemipmaps) + : LLViewerTexture(raw, usemipmaps) +{ + init(TRUE) ; +} + +LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps) + : LLViewerTexture(id, usemipmaps), + mLocalFileName(full_path) +{ + init(TRUE) ; + generateGLTexture() ; +} + +void LLViewerFetchedTexture::init(bool firstinit) +{ + mOrigWidth = 0; + mOrigHeight = 0; + mNeedsAux = FALSE; + mRequestedDiscardLevel = -1; + mRequestedDownloadPriority = 0.f; + mFullyLoaded = FALSE; + mDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; + mMinDesiredDiscardLevel = MAX_DISCARD_LEVEL + 1; + + mDecodingAux = FALSE; + + mKnownDrawWidth = 0; + mKnownDrawHeight = 0; + + 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; + + mNeedsCreateTexture = FALSE; + + mIsRawImageValid = FALSE; + mRawDiscardLevel = INVALID_DISCARD_LEVEL; + mMinDiscardLevel = 0; + + mHasFetcher = FALSE; + mIsFetching = FALSE; + mFetchState = 0; + mFetchPriority = 0; + mDownloadProgress = 0.f; + mFetchDeltaTime = 999999.f; + mDecodeFrame = 0; + mVisibleFrame = 0; + mForSculpt = FALSE ; + mIsFetched = FALSE ; +} + +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 ); + delete entryp; + } + mLoadedCallbackList.clear(); + mNeedsAux = FALSE; + + // Clean up image data + destroyRawImage(); +} + +void LLViewerFetchedTexture::setForSculpt() +{ + mForSculpt = TRUE ; +} + +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 ; + } +} + +// virtual +void LLViewerFetchedTexture::dump() +{ + LLViewerTexture::dump(); + + llinfos << "LLViewerFetchedTexture" + << " mIsMissingAsset " << (S32)mIsMissingAsset + << " mFullWidth " << mFullWidth + << " mFullHeight " << mFullHeight + << " mOrigWidth" << mOrigWidth + << " mOrigHeight" << mOrigHeight + << 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 ; +} + +// 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 (!mLocalFileName.empty()) + { + 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(); + } + 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; + } + + res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename); + setActive() ; + } + + // + // Iterate through the list of image loading callbacks to see + // what sort of data they need. + // + // *TODO: Fix image callback code + BOOL imageraw_callbacks = FALSE; + for(callback_list_t::iterator iter = mLoadedCallbackList.begin(); + iter != mLoadedCallbackList.end(); ) + { + LLLoadedCallbackEntry *entryp = *iter++; + if (entryp->mNeedsImageRaw) + { + imageraw_callbacks = TRUE; + break; + } + } + + if (!imageraw_callbacks) + { + mNeedsAux = FALSE; + destroyRawImage(); + } + return res; +} + +// Call with 0,0 to turn this feature off. +void LLViewerFetchedTexture::setKnownDrawSize(S32 width, S32 height) +{ + mKnownDrawWidth = width; + mKnownDrawHeight = height; + addTextureStats((F32)(width * height)); +} + +//virtual +void LLViewerFetchedTexture::processTextureStats() +{ + if(mFullyLoaded)//already loaded + { + return ; + } + + if(!mFullWidth || !mFullHeight) + { + mDesiredDiscardLevel = getMaxDiscardLevel() ; + } + else + { + 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 + } + + if(getDiscardLevel() >= 0 && (getDiscardLevel() <= mDesiredDiscardLevel)) + { + mFullyLoaded = TRUE ; + } + } +} + +//texture does not have any data, so we don't know the size of the image, treat it like 32 * 32. +F32 LLViewerFetchedTexture::calcDecodePriorityForUnknownTexture(F32 pixel_priority) +{ + 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 + 1; + ddiscard = llclamp(ddiscard, 1, 9); + + return ddiscard*100000.f; +} + +F32 LLViewerFetchedTexture::calcDecodePriority() +{ +#ifndef LL_RELEASE_FOR_DOWNLOAD + if (mID == LLAppViewer::getTextureFetch()->mDebugID) + { + LLAppViewer::getTextureFetch()->mDebugCount++; // for setting breakpoints + } +#endif + + if(mFullyLoaded)//already loaded for static texture + { + return -4.0f ; //alreay fetched + } + + if (mNeedsCreateTexture) + { + return mDecodePriority; // no change while waiting to create + } + + F32 priority; + S32 cur_discard = getDiscardLevel(); + bool have_all_data = (cur_discard >= 0 && (cur_discard <= mDesiredDiscardLevel)); + F32 pixel_priority = fsqrtf(mMaxVirtualSize); + const S32 MIN_NOT_VISIBLE_FRAMES = 30; // NOTE: this function is not called every frame + mDecodeFrame++; + if (pixel_priority > 0.f) + { + mVisibleFrame = mDecodeFrame; + } + + if (mIsMissingAsset) + { + priority = 0.0f; + } + else if (mDesiredDiscardLevel > getMaxDiscardLevel()) + { + // Don't decode anything we don't need + priority = -1.0f; + } + else if (mBoostLevel == LLViewerTexture::BOOST_UI && !have_all_data) + { + priority = 1.f; + } + else if (pixel_priority <= 0.f && !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 if (mVisibleFrame == 0 || (mDecodeFrame - mVisibleFrame > MIN_NOT_VISIBLE_FRAMES)) + { + // Don't decode anything that isn't visible unless it's important + priority = -2.0f; + } + else + { + // Leave the priority as-is + return mDecodePriority; + } + } + else if (cur_discard < 0) + { + priority = calcDecodePriorityForUnknownTexture(pixel_priority) ; + } + else if ((mMinDiscardLevel > 0) && (cur_discard <= mMinDiscardLevel)) + { + // larger mips are corrupted + priority = -3.0f; + } + else if (cur_discard <= mDesiredDiscardLevel) + { + priority = -4.0f; + } + else + { + // priority range = 100000-400000 + S32 ddiscard = cur_discard - mDesiredDiscardLevel; + if (getDontDiscard()) + { + ddiscard+=2; + } + else if (mGLTexturep.notNull() && !mGLTexturep->getBoundRecently() && mBoostLevel == 0) + { + ddiscard-=2; + } + ddiscard = llclamp(ddiscard, 0, 4); + priority = ddiscard*100000.f; + } + if (priority > 0.0f) + { + pixel_priority = llclamp(pixel_priority, 0.0f, priority-1.f); // priority range = 100000-900000 + if ( mBoostLevel > BOOST_HIGH) + { + priority = 1000000.f + pixel_priority + 1000.f * mBoostLevel; + } + else + { + priority += 0.f + pixel_priority + 1000.f * mBoostLevel; + } + } + return priority; +} +//============================================================================ + +void LLViewerFetchedTexture::setDecodePriority(F32 priority) +{ + llassert(!mInImageList); + mDecodePriority = priority; +} + +bool LLViewerFetchedTexture::updateFetch() +{ + 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 = getDiscardLevel() ; + S32 desired_discard = getDesiredDiscardLevel(); + F32 decode_priority = getDecodePriority(); + decode_priority = llmax(decode_priority, 0.0f); + + 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; + } + else + { + mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); + } + + // We may have data ready regardless of whether or not we are finished (e.g. waiting on write) + if (mRawImage.notNull()) + { + if(LLViewerTextureManager::sTesterp) + { + mIsFetched = TRUE ; + LLViewerTextureManager::sTesterp->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID)) ; + } + mRawDiscardLevel = fetch_discard; + if ((mRawImage->getDataSize() > 0 && mRawDiscardLevel >= 0) && + (current_discard < 0 || mRawDiscardLevel < current_discard)) + { + 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; + gTextureList.mCreateTextureList.insert(this); + mNeedsCreateTexture = TRUE; + mFullWidth = mRawImage->getWidth() << mRawDiscardLevel; + mFullHeight = mRawImage->getHeight() << mRawDiscardLevel; + } + 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.isNull()) + { + // We have data, but our fetch failed to return raw data + // *TODO: FIgure out why this is happening and fix it + destroyRawImage(); + } + } + else + { + 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 (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 (current_discard >= 0) + { + w = mGLTexturep->getWidth(0); + h = mGLTexturep->getHeight(0); + c = mComponents; + } + if (!mDontDiscard) + { + if (mBoostLevel == 0) + { + desired_discard = llmax(desired_discard, current_discard-1); + } + else + { + desired_discard = llmax(desired_discard, current_discard-2); + } + } + + // bypass texturefetch directly by pulling from LLTextureCache + bool fetch_request_created = false; + if (mLocalFileName.empty()) + { + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(getID(), getTargetHost(), decode_priority, + w, h, c, desired_discard, + needsAux()); + } + else + { + fetch_request_created = LLAppViewer::getTextureFetch()->createRequest(mLocalFileName, getID(),getTargetHost(), decode_priority, + w, h, c, desired_discard, + needsAux()); + } + + if (fetch_request_created) + { + mHasFetcher = TRUE; + mIsFetching = TRUE; + mRequestedDiscardLevel = desired_discard; + mFetchState = LLAppViewer::getTextureFetch()->getFetchState(mID, mDownloadProgress, mRequestedDownloadPriority, + mFetchPriority, mFetchDeltaTime, mRequestDeltaTime); + } + + // 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() +{ + llwarns << mLocalFileName << " " << mID << ": Marking image as missing" << llendl; + if (mHasFetcher) + { + LLAppViewer::getTextureFetch()->deleteRequest(getID(), true); + mHasFetcher = FALSE; + mIsFetching = FALSE; + 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) +{ + // + // 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); + } + LLLoadedCallbackEntry* entryp = new LLLoadedCallbackEntry(loaded_callback, discard_level, keep_imageraw, userdata); + mLoadedCallbackList.push_back(entryp); + mNeedsAux |= needs_aux; + 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; + } +} + +bool LLViewerFetchedTexture::doLoadedCallbacks() +{ + if (mNeedsCreateTexture) + { + 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); + } + + 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(); + readBackRawImage(gl_discard); + 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. + + 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; + if (final) + { + //llinfos << "Final!" << 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)) + { + 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 ; +} + +// Was in LLImageGL +LLImageRaw* LLViewerFetchedTexture::readBackRawImage(S8 discard_level) +{ + llassert_always(mGLTexturep.notNull()) ; + llassert_always(discard_level >= 0); + llassert_always(mComponents > 0); + if (mRawImage.notNull()) + { + llerrs << "called with existing mRawImage" << llendl; + mRawImage = NULL; + } + mRawImage = new LLImageRaw(mGLTexturep->getWidth(discard_level), mGLTexturep->getHeight(discard_level), mComponents); + sRawCount++; + mRawDiscardLevel = discard_level; + mGLTexturep->readBackRaw(mRawDiscardLevel, mRawImage, false); + mIsRawImageValid = TRUE; + + return mRawImage; +} + +void LLViewerFetchedTexture::destroyRawImage() +{ + if (mRawImage.notNull()) sRawCount--; + if (mAuxRawImage.notNull()) sAuxCount--; + mRawImage = NULL; + mAuxRawImage = NULL; + mIsRawImageValid = FALSE; + mRawDiscardLevel = INVALID_DISCARD_LEVEL; +} +//---------------------------------------------------------------------------------------------- +//end of LLViewerFetchedTexture +//---------------------------------------------------------------------------------------------- + +//---------------------------------------------------------------------------------------------- +//start of LLViewerLODTexture +//---------------------------------------------------------------------------------------------- +LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, BOOL usemipmaps) + : LLViewerFetchedTexture(id, usemipmaps) +{ + init(TRUE) ; +} + +LLViewerLODTexture::LLViewerLODTexture(const std::string& full_path, const LLUUID& id, BOOL usemipmaps) + : LLViewerFetchedTexture(full_path, 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 ; +} + +// This is gauranteed to get called periodically for every texture +//virtual +void LLViewerLODTexture::processTextureStats() +{ + // Generate the request priority and render priority + 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); + + S32 fullwidth = llmin(mFullWidth,(S32)MAX_IMAGE_SIZE_DEFAULT); + S32 fullheight = llmin(mFullHeight,(S32)MAX_IMAGE_SIZE_DEFAULT); + mTexelsPerImage = (F32)fullwidth * fullheight; + + 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 (mBoostLevel == LLViewerTexture::BOOST_UI || + mBoostLevel == LLViewerTexture::BOOST_PREVIEW || + mBoostLevel == LLViewerTexture::BOOST_AVATAR_SELF) // JAMESDEBUG what about AVATAR_BAKED_SELF? + { + discard_level = 0; // full res + } + else*/ 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 ((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_HIGH) + { + static const F32 discard_bias = -.5f; // Must be < 1 or highest discard will never load! + discard_level += discard_bias; + discard_level += sDesiredDiscardBias; + discard_level *= sDesiredDiscardScale; // scale + } + discard_level = floorf(discard_level); +// discard_level -= (gTextureList.mVideoMemorySetting>>1); // more video ram = higher detail + + 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. + // + + BOOL increase_discard = FALSE; + S32 current_discard = getDiscardLevel(); + if ((sDesiredDiscardBias > 0.0f) && + (current_discard >= 0 && mDesiredDiscardLevel >= current_discard)) + { + if ( BYTES_TO_MEGA_BYTES(sBoundTextureMemoryInBytes) > sMaxBoundTextureMemInMegaBytes * texmem_middle_bound_scale) + { + // Limit the amount of GL memory bound each frame + if (mDesiredDiscardLevel > current_discard) + { + increase_discard = TRUE; + } + } + if ( BYTES_TO_MEGA_BYTES(sTotalTextureMemoryInBytes) > sMaxTotalTextureMemInMegaBytes*texmem_middle_bound_scale) + { + // Only allow GL to have 2x the video card memory + if (!mGLTexturep->getBoundRecently()) + { + increase_discard = TRUE; + } + } + if (increase_discard) + { + // llinfos << "DISCARDED: " << mID << " Discard: " << current_discard << llendl; + sBoundTextureMemoryInBytes -= mGLTexturep->mTextureMemory; + sTotalTextureMemoryInBytes -= mGLTexturep->mTextureMemory; + // Increase the discard level (reduce the texture res) + S32 new_discard = current_discard+1; + mGLTexturep->setDiscardLevel(new_discard); + sBoundTextureMemoryInBytes += mGLTexturep->mTextureMemory; + sTotalTextureMemoryInBytes += mGLTexturep->mTextureMemory; + if(LLViewerTextureManager::sTesterp) + { + LLViewerTextureManager::sTesterp->setStablizingTime() ; + } + } + } + } +} + +//---------------------------------------------------------------------------------------------- +//end of LLViewerLODTexture +//---------------------------------------------------------------------------------------------- + +//---------------------------------------------------------------------------------------------- +//start of LLViewerMediaTexture +//---------------------------------------------------------------------------------------------- +//static +void LLViewerMediaTexture::updateClass() +{ + static const F32 MAX_INACTIVE_TIME = 30.f ; + + for(media_map_t::iterator iter = sMediaMap.begin() ; iter != sMediaMap.end(); ) + { + LLViewerMediaTexture* mediap = iter->second; + ++iter ; + + if(mediap->getNumRefs() == 1 && mediap->getLastReferencedTimer()->getElapsedTimeF32() > MAX_INACTIVE_TIME) //one by sMediaMap + { + sMediaMap.erase(mediap->getID()) ; + } + } +} + +LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LLImageGL* gl_image) + : LLViewerTexture(id, usemipmaps) +{ + sMediaMap.insert(std::make_pair(id, this)); + + mGLTexturep = gl_image ; + if(mGLTexturep.isNull()) + { + generateGLTexture() ; + } + mGLTexturep->setNeedsAlphaAndPickMask(FALSE) ; + + mIsPlaying = FALSE ; +} + +void LLViewerMediaTexture::reinit(BOOL usemipmaps /* = TRUE */) +{ + mGLTexturep = NULL ; + init(false); + mUseMipMaps = usemipmaps ; + mIsPlaying = FALSE ; + getLastReferencedTimer()->reset() ; + + generateGLTexture() ; + mGLTexturep->setNeedsAlphaAndPickMask(FALSE) ; +} + +void LLViewerMediaTexture::setUseMipMaps(BOOL mipmap) +{ + mUseMipMaps = mipmap; + + if(mGLTexturep.notNull()) + { + mGLTexturep->setUseMipMaps(mipmap) ; + } +} + +//virtual +S8 LLViewerMediaTexture::getType() const +{ + return LLViewerTexture::MEDIA_TEXTURE ; +} + +void LLViewerMediaTexture::setOldTexture(LLViewerTexture* tex) +{ + mOldTexturep = tex ; +} + +LLViewerTexture* LLViewerMediaTexture::getOldTexture() const +{ + return mOldTexturep ; +} +//---------------------------------------------------------------------------------------------- +//end of LLViewerMediaTexture +//---------------------------------------------------------------------------------------------- + +//---------------------------------------------------------------------------------------------- +//start of LLTexturePipelineTester +//---------------------------------------------------------------------------------------------- +LLTexturePipelineTester::LLTexturePipelineTester() : + LLMetricPerformanceTester("TextureTester", FALSE) +{ + addMetricString("TotalBytesLoaded") ; + addMetricString("TotalBytesLoadedFromCache") ; + addMetricString("TotalBytesLoadedForLargeImage") ; + addMetricString("TotalBytesLoadedForSculpties") ; + addMetricString("StartFetchingTime") ; + addMetricString("TotalGrayTime") ; + addMetricString("TotalStablizingTime") ; + addMetricString("StartTimeLoadingSculpties") ; + addMetricString("EndTimeLoadingSculpties") ; + + addMetricString("Time") ; + addMetricString("TotalBytesBound") ; + addMetricString("TotalBytesBoundForLargeImage") ; + addMetricString("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->isForSculpt()) + { + 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 > 0.0001f && (t - mTotalStablizingTime) < 0.0001f) + { + //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 > 0.0001f) //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 ; +} +//---------------------------------------------------------------------------------------------- +//end of LLTexturePipelineTester +//---------------------------------------------------------------------------------------------- + |