Sync light state, bound shader, and various gl context states similarly to render matrices. Texture handles now refcounted, as multiple viewer textures could ref the same handle (cubemaps do this) Clean up gl extension loading a bit. Not necessary, but only look for ARB variants if not included in current core version. Removed unused extensions. Use core shader api if supported, else use ARB. (FN signatures are identical. Just doing some pointer substitution to ARB if not core.) Attempt at improving VBO update batching. Subdata updates better batched to gether per-frame. There's probably other stuff I forgot that is in this changeset, too. Todo: Fix lightstate assertion when toggling fullscreen with shaders off.
4142 lines
114 KiB
C++
4142 lines
114 KiB
C++
/**
|
|
* @file llviewertexture.cpp
|
|
* @brief Object which handles a received image (and associated texture(s))
|
|
*
|
|
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/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 "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 "lltextureentry.h"
|
|
#include "lltexturemanagerbridge.h"
|
|
#include "llmediaentry.h"
|
|
#include "llvovolume.h"
|
|
#include "llviewermedia.h"
|
|
#include "lltexturecache.h"
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// extern
|
|
const S32Megabytes gMinVideoRam(32);
|
|
const S32Megabytes gMaxVideoRam(2048);
|
|
|
|
|
|
// statics
|
|
LLPointer<LLViewerTexture> LLViewerTexture::sNullImagep = NULL;
|
|
LLPointer<LLViewerTexture> LLViewerTexture::sBlackImagep = NULL;
|
|
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sMissingAssetImagep = NULL;
|
|
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sWhiteImagep = NULL;
|
|
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sDefaultImagep = NULL;
|
|
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sSmokeImagep = NULL;
|
|
LLPointer<LLViewerFetchedTexture> LLViewerFetchedTexture::sFlatNormalImagep = NULL;
|
|
LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap;
|
|
#if 0
|
|
LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL;
|
|
#endif
|
|
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;
|
|
S64Bytes LLViewerTexture::sBoundTextureMemory;
|
|
S64Bytes LLViewerTexture::sTotalTextureMemory;
|
|
S32Megabytes LLViewerTexture::sMaxBoundTextureMemory;
|
|
S32Megabytes LLViewerTexture::sMaxTotalTextureMem;
|
|
S64Bytes LLViewerTexture::sMaxDesiredTextureMem;
|
|
S8 LLViewerTexture::sCameraMovingDiscardBias = 0;
|
|
F32 LLViewerTexture::sCameraMovingBias = 0.0f;
|
|
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;
|
|
F32 LLViewerTexture::sTexelPixelRatio = 1.0f;
|
|
|
|
LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
LLViewerFetchedTexture* LLViewerTextureManager::findFetchedTexture(const LLUUID& id)
|
|
{
|
|
return gTextureList.findImage(id);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
LL_ERRS() << "not a fetched texture type: " << type << LL_ENDL;
|
|
}
|
|
|
|
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(LLGLTexture::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(LLGLTexture::LOCAL);
|
|
}
|
|
return tex;
|
|
}
|
|
LLPointer<LLViewerTexture> LLViewerTextureManager::getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps)
|
|
{
|
|
LLPointer<LLViewerTexture> tex = new LLViewerTexture(raw, usemipmaps);
|
|
tex->setCategory(LLGLTexture::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(LLGLTexture::LOCAL);
|
|
}
|
|
return tex;
|
|
}
|
|
|
|
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTexture(
|
|
const LLUUID &image_id,
|
|
FTType f_type,
|
|
BOOL usemipmaps,
|
|
LLViewerTexture::EBoostLevel boost_priority,
|
|
S8 texture_type,
|
|
LLGLint internal_format,
|
|
LLGLenum primary_format,
|
|
LLHost request_from_host)
|
|
{
|
|
return gTextureList.getImage(image_id, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, request_from_host);
|
|
}
|
|
|
|
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromFile(
|
|
const std::string& filename,
|
|
FTType f_type,
|
|
BOOL usemipmaps,
|
|
LLViewerTexture::EBoostLevel boost_priority,
|
|
S8 texture_type,
|
|
LLGLint internal_format,
|
|
LLGLenum primary_format,
|
|
const LLUUID& force_id)
|
|
{
|
|
return gTextureList.getImageFromFile(filename, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
|
|
}
|
|
|
|
//static
|
|
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromUrl(const std::string& url,
|
|
FTType f_type,
|
|
BOOL usemipmaps,
|
|
LLViewerTexture::EBoostLevel boost_priority,
|
|
S8 texture_type,
|
|
LLGLint internal_format,
|
|
LLGLenum primary_format,
|
|
const LLUUID& force_id
|
|
)
|
|
{
|
|
return gTextureList.getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id);
|
|
}
|
|
|
|
LLViewerFetchedTexture* LLViewerTextureManager::getFetchedTextureFromHost(const LLUUID& image_id, FTType f_type, LLHost host)
|
|
{
|
|
return gTextureList.getImageFromHost(image_id, f_type, host);
|
|
}
|
|
|
|
// Create a bridge to the viewer texture manager.
|
|
class LLViewerTextureManagerBridge : public LLTextureManagerBridge
|
|
{
|
|
/*virtual*/ LLPointer<LLGLTexture> getLocalTexture(BOOL usemipmaps = TRUE, BOOL generate_gl_tex = TRUE)
|
|
{
|
|
return LLViewerTextureManager::getLocalTexture(usemipmaps, generate_gl_tex);
|
|
}
|
|
|
|
/*virtual*/ LLPointer<LLGLTexture> getLocalTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps, BOOL generate_gl_tex = TRUE)
|
|
{
|
|
return LLViewerTextureManager::getLocalTexture(width, height, components, usemipmaps, generate_gl_tex);
|
|
}
|
|
|
|
/*virtual*/ LLGLTexture* getFetchedTexture(const LLUUID &image_id)
|
|
{
|
|
return LLViewerTextureManager::getFetchedTexture(image_id);
|
|
}
|
|
};
|
|
|
|
|
|
void LLViewerTextureManager::init()
|
|
{
|
|
{
|
|
LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,3);
|
|
raw->clear(0x77, 0x77, 0x77, 0xFF);
|
|
LLViewerTexture::sNullImagep = LLViewerTextureManager::getLocalTexture(raw.get(), TRUE);
|
|
}
|
|
|
|
const S32 dim = 128;
|
|
LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
|
|
U8* data = image_raw->getData();
|
|
|
|
memset(data, 0, dim * dim * 3);
|
|
LLViewerTexture::sBlackImagep = LLViewerTextureManager::getLocalTexture(image_raw.get(), TRUE);
|
|
|
|
#if 1
|
|
LLPointer<LLViewerFetchedTexture> imagep = LLViewerTextureManager::getFetchedTexture(IMG_DEFAULT);
|
|
LLViewerFetchedTexture::sDefaultImagep = imagep;
|
|
|
|
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, LLGLTexture::BOOST_UI);
|
|
#endif
|
|
LLViewerFetchedTexture::sDefaultImagep->dontDiscard();
|
|
LLViewerFetchedTexture::sDefaultImagep->setCategory(LLGLTexture::OTHER);
|
|
|
|
LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI);
|
|
LLViewerFetchedTexture::sSmokeImagep->setNoDelete();
|
|
|
|
LLViewerTexture::initClass();
|
|
|
|
// Create a texture manager bridge.
|
|
gTextureManagerBridgep = new LLViewerTextureManagerBridge();
|
|
|
|
#if 0
|
|
if (LLMetricPerformanceTesterBasic::isMetricLogRequested(sTesterName) && !LLMetricPerformanceTesterBasic::getTester(sTesterName))
|
|
{
|
|
sTesterp = new LLTexturePipelineTester();
|
|
if (!sTesterp->isValid())
|
|
{
|
|
delete sTesterp;
|
|
sTesterp = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void LLViewerTextureManager::cleanup()
|
|
{
|
|
stop_glerror();
|
|
|
|
delete gTextureManagerBridgep;
|
|
LLImageGL::sDefaultGLTexture = NULL;
|
|
LLViewerTexture::sNullImagep = NULL;
|
|
LLViewerTexture::sBlackImagep = NULL;
|
|
LLViewerFetchedTexture::sDefaultImagep = NULL;
|
|
LLViewerFetchedTexture::sSmokeImagep = NULL;
|
|
LLViewerFetchedTexture::sMissingAssetImagep = NULL;
|
|
LLViewerFetchedTexture::sWhiteImagep = NULL;
|
|
LLViewerFetchedTexture::sFlatNormalImagep = NULL;
|
|
|
|
LLViewerMediaTexture::cleanUpClass();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------------------------
|
|
//start of LLViewerTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
// static
|
|
void LLViewerTexture::initClass()
|
|
{
|
|
LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture();
|
|
sTexelPixelRatio = gSavedSettings.getF32("TexelPixelRatio");
|
|
if(gAuditTexture)
|
|
{
|
|
LLImageGL::setHighlightTexture(LLViewerTexture::OTHER);
|
|
}
|
|
}
|
|
|
|
// 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 LLTrace::BlockTimerStatHandle FTM_TEXTURE_MEMORY_CHECK("Memory Check");
|
|
//static
|
|
bool LLViewerTexture::isMemoryForTextureLow()
|
|
{
|
|
const F32 WAIT_TIME = 1.0f; //second
|
|
static LLFrameTimer timer;
|
|
|
|
if(timer.getElapsedTimeF32() < WAIT_TIME) //call this once per second.
|
|
{
|
|
return false;
|
|
}
|
|
timer.reset();
|
|
|
|
LL_RECORD_BLOCK_TIME(FTM_TEXTURE_MEMORY_CHECK);
|
|
|
|
static const S32Megabytes MIN_FREE_TEXTURE_MEMORY(5); //MB
|
|
static const S32Megabytes MIN_FREE_MAIN_MEMORY(100); //MB
|
|
|
|
bool low_mem = false;
|
|
if (gGLManager.mHasATIMemInfo)
|
|
{
|
|
S32 meminfo[4];
|
|
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, meminfo);
|
|
|
|
if((S32Megabytes)meminfo[0] < MIN_FREE_TEXTURE_MEMORY)
|
|
{
|
|
low_mem = true;
|
|
}
|
|
|
|
if(!low_mem) //check main memory, only works for windows.
|
|
{
|
|
LLMemory::updateMemoryInfo();
|
|
if(LLMemory::getAvailableMemKB() < MIN_FREE_TEXTURE_MEMORY)
|
|
{
|
|
low_mem = true;
|
|
}
|
|
}
|
|
}
|
|
#if 0 //ignore nVidia cards
|
|
else if (gGLManager.mHasNVXMemInfo)
|
|
{
|
|
S32 free_memory;
|
|
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &free_memory);
|
|
|
|
if(free_memory / 1024 < MIN_FREE_TEXTURE_MEMORY)
|
|
{
|
|
low_mem = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return low_mem;
|
|
}
|
|
|
|
static LLTrace::BlockTimerStatHandle FTM_TEXTURE_UPDATE_MEDIA("Media");
|
|
#if 0
|
|
static LLTrace::BlockTimerStatHandle FTM_TEXTURE_UPDATE_TEST("Test");
|
|
#endif
|
|
//static
|
|
void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity)
|
|
{
|
|
sCurrentTime = gFrameTimeSeconds;
|
|
|
|
#if 0
|
|
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
|
|
if (tester)
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_TEXTURE_UPDATE_TEST);
|
|
tester->update();
|
|
}
|
|
#endif
|
|
{
|
|
LL_RECORD_BLOCK_TIME(FTM_TEXTURE_UPDATE_MEDIA);
|
|
LLViewerMediaTexture::updateClass();
|
|
}
|
|
|
|
sBoundTextureMemory = LLImageGL::sBoundTextureMemory;
|
|
sTotalTextureMemory = LLImageGL::sGlobalTextureMemory;
|
|
sMaxBoundTextureMemory = S32Megabytes(gTextureList.getMaxResidentTexMem());
|
|
sMaxTotalTextureMem = S32Megabytes(gTextureList.getMaxTotalTextureMem());
|
|
sMaxDesiredTextureMem = sMaxTotalTextureMem; //in Bytes, by default and when total used texture memory is small.
|
|
|
|
if (sBoundTextureMemory >= sMaxBoundTextureMemory ||
|
|
sTotalTextureMemory >= sMaxTotalTextureMem)
|
|
{
|
|
//when texture memory overflows, lower down the threshold to release the textures more aggressively.
|
|
sMaxDesiredTextureMem = llmin(sMaxDesiredTextureMem * 0.75f, F32Bytes(gMaxVideoRam));
|
|
|
|
// 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(sEvaluationTimer.getElapsedTimeF32() > discard_delta_time && isMemoryForTextureLow())
|
|
{
|
|
sDesiredDiscardBias += discard_bias_delta;
|
|
sEvaluationTimer.reset();
|
|
}
|
|
else if (sDesiredDiscardBias > 0.0f &&
|
|
sBoundTextureMemory < sMaxBoundTextureMemory * texmem_lower_bound_scale &&
|
|
sTotalTextureMemory < sMaxTotalTextureMem * 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);
|
|
|
|
F32 camera_moving_speed = LLViewerCamera::getInstance()->getAverageSpeed();
|
|
F32 camera_angular_speed = LLViewerCamera::getInstance()->getAverageAngularSpeed();
|
|
sCameraMovingBias = llmax(0.2f * camera_moving_speed, 2.0f * camera_angular_speed - 1);
|
|
sCameraMovingDiscardBias = (S8)(sCameraMovingBias);
|
|
|
|
LLViewerTexture::sFreezeImageScalingDown = (sBoundTextureMemory < 0.75f * sMaxBoundTextureMemory * texmem_middle_bound_scale) &&
|
|
(sTotalTextureMemory < 0.75f * sMaxTotalTextureMem * texmem_middle_bound_scale);
|
|
}
|
|
|
|
//end of static functions
|
|
//-------------------------------------------------------------------------------------------
|
|
const U32 LLViewerTexture::sCurrentFileVersion = 1;
|
|
|
|
LLViewerTexture::LLViewerTexture(BOOL usemipmaps) :
|
|
LLGLTexture(usemipmaps)
|
|
{
|
|
init(true);
|
|
|
|
mID.generate();
|
|
sImageCount++;
|
|
}
|
|
|
|
LLViewerTexture::LLViewerTexture(const LLUUID& id, BOOL usemipmaps) :
|
|
LLGLTexture(usemipmaps),
|
|
mID(id)
|
|
{
|
|
init(true);
|
|
|
|
sImageCount++;
|
|
}
|
|
|
|
LLViewerTexture::LLViewerTexture(const U32 width, const U32 height, const U8 components, BOOL usemipmaps) :
|
|
LLGLTexture(width, height, components, usemipmaps)
|
|
{
|
|
init(true);
|
|
|
|
mID.generate();
|
|
sImageCount++;
|
|
}
|
|
|
|
LLViewerTexture::LLViewerTexture(const LLImageRaw* raw, BOOL usemipmaps) :
|
|
LLGLTexture(raw, usemipmaps)
|
|
{
|
|
init(true);
|
|
|
|
mID.generate();
|
|
sImageCount++;
|
|
}
|
|
|
|
LLViewerTexture::~LLViewerTexture()
|
|
{
|
|
// LL_DEBUGS("Avatar") << mID << LL_ENDL;
|
|
cleanup();
|
|
sImageCount--;
|
|
}
|
|
|
|
// virtual
|
|
void LLViewerTexture::init(bool firstinit)
|
|
{
|
|
mSelectedTime = 0.f;
|
|
mMaxVirtualSize = 0.f;
|
|
mNeedsGLTexture = FALSE;
|
|
mMaxVirtualSizeResetInterval = 1;
|
|
mMaxVirtualSizeResetCounter = mMaxVirtualSizeResetInterval;
|
|
mAdditionalDecodePriority = 0.f;
|
|
mParcelMedia = NULL;
|
|
|
|
mNumVolumes = 0;
|
|
mFaceList[LLRender::DIFFUSE_MAP].clear();
|
|
mFaceList[LLRender::NORMAL_MAP].clear();
|
|
mFaceList[LLRender::SPECULAR_MAP].clear();
|
|
mNumFaces[LLRender::DIFFUSE_MAP] =
|
|
mNumFaces[LLRender::NORMAL_MAP] =
|
|
mNumFaces[LLRender::SPECULAR_MAP] = 0;
|
|
mVolumeList.clear();
|
|
}
|
|
|
|
//virtual
|
|
S8 LLViewerTexture::getType() const
|
|
{
|
|
return LLViewerTexture::LOCAL_TEXTURE;
|
|
}
|
|
|
|
void LLViewerTexture::cleanup()
|
|
{
|
|
notifyAboutMissingAsset();
|
|
|
|
mFaceList[LLRender::DIFFUSE_MAP].clear();
|
|
mFaceList[LLRender::NORMAL_MAP].clear();
|
|
mFaceList[LLRender::SPECULAR_MAP].clear();
|
|
mVolumeList.clear();
|
|
}
|
|
|
|
void LLViewerTexture::notifyAboutCreatingTexture()
|
|
{
|
|
for(U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
|
|
{
|
|
for(U32 f = 0; f < mNumFaces[ch]; f++)
|
|
{
|
|
mFaceList[ch][f]->notifyAboutCreatingTexture(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LLViewerTexture::notifyAboutMissingAsset()
|
|
{
|
|
for(U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
|
|
{
|
|
for(U32 f = 0; f < mNumFaces[ch]; f++)
|
|
{
|
|
mFaceList[ch][f]->notifyAboutMissingAsset(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
void LLViewerTexture::dump()
|
|
{
|
|
LLGLTexture::dump();
|
|
|
|
LL_INFOS() << "LLViewerTexture"
|
|
<< " mID " << mID
|
|
<< LL_ENDL;
|
|
}
|
|
|
|
void LLViewerTexture::setBoostLevel(S32 level)
|
|
{
|
|
if(mBoostLevel != level)
|
|
{
|
|
mBoostLevel = level;
|
|
if(mBoostLevel != LLViewerTexture::BOOST_NONE &&
|
|
mBoostLevel != LLViewerTexture::BOOST_SELECTED)
|
|
{
|
|
setNoDelete();
|
|
}
|
|
}
|
|
|
|
if (mBoostLevel == LLViewerTexture::BOOST_SELECTED)
|
|
{
|
|
mSelectedTime = gFrameTimeSeconds;
|
|
}
|
|
|
|
}
|
|
|
|
bool LLViewerTexture::isActiveFetching()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
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)
|
|
{
|
|
LL_WARNS() << "LLViewerTexture::bindDefaultImage failed." << LL_ENDL;
|
|
}
|
|
stop_glerror();
|
|
|
|
//check if there is cached raw image and switch to it if possible
|
|
switchToCachedImage();
|
|
|
|
#if 0
|
|
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
|
|
if (tester)
|
|
{
|
|
tester->updateGrayTextureBinding();
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
virtual_size *= sTexelPixelRatio;
|
|
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(U32 ch, LLFace* facep)
|
|
{
|
|
llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
|
|
|
|
if(mNumFaces[ch] >= mFaceList[ch].size())
|
|
{
|
|
mFaceList[ch].resize(2 * mNumFaces[ch] + 1);
|
|
}
|
|
mFaceList[ch][mNumFaces[ch]] = facep;
|
|
facep->setIndexInTex(ch, mNumFaces[ch]);
|
|
mNumFaces[ch]++;
|
|
mLastFaceListUpdateTimer.reset();
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerTexture::removeFace(U32 ch, LLFace* facep)
|
|
{
|
|
if (ch >= LLRender::NUM_TEXTURE_CHANNELS) // Suppress Linux warning, this should NEVER happen!
|
|
{
|
|
LL_ERRS() << ch << " >= LLRender::NUM_TEXTURE_CHANNELS!!!" << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
if(mNumFaces[ch] > 1)
|
|
{
|
|
S32 index = facep->getIndexInTex(ch);
|
|
llassert(index < (S32)mFaceList[ch].size());
|
|
llassert(index < (S32)mNumFaces[ch]);
|
|
mFaceList[ch][index] = mFaceList[ch][--mNumFaces[ch]];
|
|
mFaceList[ch][index]->setIndexInTex(ch, index);
|
|
}
|
|
else
|
|
{
|
|
mFaceList[ch].clear();
|
|
mNumFaces[ch] = 0;
|
|
}
|
|
mLastFaceListUpdateTimer.reset();
|
|
}
|
|
|
|
S32 LLViewerTexture::getTotalNumFaces() const
|
|
{
|
|
S32 ret = 0;
|
|
|
|
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
|
|
{
|
|
ret += mNumFaces[i];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
S32 LLViewerTexture::getNumFaces(U32 ch) const
|
|
{
|
|
llassert(ch < LLRender::NUM_TEXTURE_CHANNELS);
|
|
return mNumFaces[ch];
|
|
}
|
|
|
|
|
|
//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();
|
|
llassert(index < (S32)mVolumeList.size());
|
|
llassert(index < (S32)mNumVolumes);
|
|
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(mLastFaceListUpdateTimer.getElapsedTimeF32() < MAX_WAIT_TIME)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (U32 i = 0; i < LLRender::NUM_TEXTURE_CHANNELS; ++i)
|
|
{
|
|
if(mNumFaces[i] + MAX_EXTRA_BUFFER_SIZE > mFaceList[i].size())
|
|
{
|
|
return;
|
|
}
|
|
|
|
mFaceList[i].erase(mFaceList[i].begin() + mNumFaces[i], mFaceList[i].end());
|
|
}
|
|
|
|
mLastFaceListUpdateTimer.reset();
|
|
}
|
|
|
|
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.
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerTexture::setCachedRawImage(S32 discard_level, LLImageRaw* imageraw)
|
|
{
|
|
//nothing here.
|
|
}
|
|
|
|
BOOL LLViewerTexture::isLargeImage()
|
|
{
|
|
return (S32)mTexelsPerImage > LLViewerTexture::sMinLargeImageSize;
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerTexture::updateBindStatsForTester()
|
|
{
|
|
#if 0
|
|
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
|
|
if (tester)
|
|
{
|
|
tester->updateTextureBindingStats(this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//end of LLViewerTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
|
|
const std::string& fttype_to_string(const FTType& fttype)
|
|
{
|
|
static const std::string ftt_unknown("FTT_UNKNOWN");
|
|
static const std::string ftt_default("FTT_DEFAULT");
|
|
static const std::string ftt_server_bake("FTT_SERVER_BAKE");
|
|
static const std::string ftt_host_bake("FTT_HOST_BAKE");
|
|
static const std::string ftt_map_tile("FTT_MAP_TILE");
|
|
static const std::string ftt_local_file("FTT_LOCAL_FILE");
|
|
static const std::string ftt_error("FTT_ERROR");
|
|
switch(fttype)
|
|
{
|
|
case FTT_UNKNOWN: return ftt_unknown; break;
|
|
case FTT_DEFAULT: return ftt_default; break;
|
|
case FTT_SERVER_BAKE: return ftt_server_bake; break;
|
|
case FTT_HOST_BAKE: return ftt_host_bake; break;
|
|
case FTT_MAP_TILE: return ftt_map_tile; break;
|
|
case FTT_LOCAL_FILE: return ftt_local_file; break;
|
|
}
|
|
return ftt_error;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//start of LLViewerFetchedTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
|
|
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, FTType f_type, const LLHost& host, BOOL usemipmaps)
|
|
: LLViewerTexture(id, usemipmaps),
|
|
mTargetHost(host)
|
|
{
|
|
init(TRUE);
|
|
mFTType = f_type;
|
|
if (mFTType == FTT_HOST_BAKE)
|
|
{
|
|
LL_WARNS() << "Unsupported fetch type " << mFTType << LL_ENDL;
|
|
}
|
|
generateGLTexture();
|
|
mGLTexturep->setNeedsAlphaAndPickMask(TRUE);
|
|
}
|
|
|
|
LLViewerFetchedTexture::LLViewerFetchedTexture(const LLImageRaw* raw, FTType f_type, BOOL usemipmaps)
|
|
: LLViewerTexture(raw, usemipmaps)
|
|
{
|
|
init(TRUE);
|
|
mFTType = f_type;
|
|
mGLTexturep->setNeedsAlphaAndPickMask(TRUE);
|
|
}
|
|
|
|
LLViewerFetchedTexture::LLViewerFetchedTexture(const std::string& url, FTType f_type, const LLUUID& id, BOOL usemipmaps)
|
|
: LLViewerTexture(id, usemipmaps),
|
|
mUrl(url)
|
|
{
|
|
init(TRUE);
|
|
mFTType = f_type;
|
|
generateGLTexture();
|
|
mGLTexturep->setNeedsAlphaAndPickMask(TRUE);
|
|
}
|
|
|
|
void LLViewerFetchedTexture::init(bool firstinit)
|
|
{
|
|
mOrigWidth = 0;
|
|
mOrigHeight = 0;
|
|
mHasAux = FALSE;
|
|
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 = 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;
|
|
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;
|
|
mKeptSavedRawImageTime = 0.f;
|
|
mLastCallBackActiveTime = 0.f;
|
|
mForceCallbackFetch = FALSE;
|
|
mFTType = FTT_UNKNOWN;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
FTType LLViewerFetchedTexture::getFTType() const
|
|
{
|
|
return mFTType;
|
|
}
|
|
|
|
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;
|
|
mSavedRawDiscardLevel = -1;
|
|
}
|
|
|
|
void LLViewerFetchedTexture::forceRefetch()
|
|
{
|
|
bool needs_aux = mNeedsAux;
|
|
bool save_raw = mForceToSaveRawImage;
|
|
S32 raw_discard = mDesiredSavedRawDiscardLevel;
|
|
F32 raw_time = mKeptSavedRawImageTime;
|
|
callback_list_t callback_list = mLoadedCallbackList;
|
|
mLoadedCallbackList.clear();
|
|
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
|
|
if(mGLTexturep)
|
|
mGLTexturep->forceToInvalidateGLTexture();
|
|
init(false);
|
|
mRawImage = NULL;
|
|
mAuxRawImage = NULL;
|
|
if(save_raw)
|
|
forceToSaveRawImage(raw_discard,raw_time);
|
|
for(callback_list_t::iterator iter = callback_list.begin();
|
|
iter != callback_list.end(); ++iter)
|
|
{
|
|
LLLoadedCallbackEntry *entryp = *iter;
|
|
setLoadedCallback(entryp->mCallback,entryp->mDesiredDiscard,entryp->mNeedsImageRaw,needs_aux,entryp->mUserData,entryp->mSourceCallbackList,entryp->mPaused);
|
|
}
|
|
}
|
|
|
|
void LLViewerFetchedTexture::setForSculpt()
|
|
{
|
|
static const S32 MAX_INTERVAL = 8; //frames
|
|
|
|
mForSculpt = TRUE;
|
|
if(isForSculptOnly() && hasGLTexture() && !getBoundRecently())
|
|
{
|
|
destroyGLTexture(); //sculpt image does not need gl texture.
|
|
mTextureState = ACTIVE;
|
|
}
|
|
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();
|
|
|
|
LL_INFOS() << "Dump : " << mID
|
|
<< ", mIsMissingAsset = " << (S32)mIsMissingAsset
|
|
<< ", mFullWidth = " << (S32)mFullWidth
|
|
<< ", mFullHeight = " << (S32)mFullHeight
|
|
<< ", mOrigWidth = " << (S32)mOrigWidth
|
|
<< ", mOrigHeight = " << (S32)mOrigHeight
|
|
<< LL_ENDL;
|
|
LL_INFOS() << " : "
|
|
<< " mFullyLoaded = " << (S32)mFullyLoaded
|
|
<< ", mFetchState = " << (S32)mFetchState
|
|
<< ", mFetchPriority = " << (S32)mFetchPriority
|
|
<< ", mDownloadProgress = " << (F32)mDownloadProgress
|
|
<< LL_ENDL;
|
|
LL_INFOS() << " : "
|
|
<< " mHasFetcher = " << (S32)mHasFetcher
|
|
<< ", mIsFetching = " << (S32)mIsFetching
|
|
<< ", mIsFetched = " << (S32)mIsFetched
|
|
<< ", mBoostLevel = " << (S32)mBoostLevel
|
|
<< LL_ENDL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// ONLY called from LLViewerFetchedTextureList
|
|
void LLViewerFetchedTexture::destroyTexture()
|
|
{
|
|
if(LLImageGL::sGlobalTextureMemory < sMaxDesiredTextureMem * 0.95f)//not ready to release unused memory.
|
|
{
|
|
return ;
|
|
}
|
|
if (mNeedsCreateTexture)//return if in the process of generating a new texture.
|
|
{
|
|
return;
|
|
}
|
|
|
|
//LL_DEBUGS("Avatar") << mID << LL_ENDL;
|
|
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 j = 0; j < LLRender::NUM_TEXTURE_CHANNELS; ++j)
|
|
{
|
|
llassert(mNumFaces[j] <= mFaceList[j].size());
|
|
|
|
for(U32 i = 0; i < mNumFaces[j]; i++)
|
|
{
|
|
mFaceList[j][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, LLImageGL::GLTextureName(), 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;
|
|
}
|
|
|
|
{
|
|
//make a duplicate in case somebody else is using this raw image
|
|
mRawImage = mRawImage->duplicate();
|
|
mRawImage->scale(w >> i, h >> i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
mNeedsCreateTexture = TRUE;
|
|
gTextureList.mCreateTextureList.insert(this);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ONLY called from LLViewerTextureList
|
|
BOOL LLViewerFetchedTexture::createTexture(LLImageGL::GLTextureName& usename)
|
|
{
|
|
if (!mNeedsCreateTexture)
|
|
{
|
|
destroyRawImage();
|
|
return FALSE;
|
|
}
|
|
mNeedsCreateTexture = FALSE;
|
|
if (mRawImage.isNull())
|
|
{
|
|
LL_WARNS() << "LLViewerTexture trying to create texture with no Raw Image" << LL_ENDL;
|
|
}
|
|
// LL_INFOS() << llformat("IMAGE Creating (%d) [%d x %d] Bytes: %d ",
|
|
// mRawDiscardLevel,
|
|
// mRawImage->getWidth(), mRawImage->getHeight(),mRawImage->getDataSize())
|
|
// << mID.getString() << LL_ENDL;
|
|
BOOL res = TRUE;
|
|
|
|
// store original size only for locally-sourced images
|
|
if (mUrl.compare(0, 7, "file://") == 0)
|
|
{
|
|
mOrigWidth = mRawImage->getWidth();
|
|
mOrigHeight = mRawImage->getHeight();
|
|
|
|
|
|
if (mBoostLevel == BOOST_PREVIEW)
|
|
{
|
|
mRawImage->biasedScaleToPowerOfTwo(1024);
|
|
}
|
|
else
|
|
{ // 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;
|
|
}
|
|
|
|
if (!mRawImage->mComment.empty())
|
|
{
|
|
// a is for uploader
|
|
// z is for time
|
|
// K is the whole thing (just coz)
|
|
std::string comment = mRawImage->getComment();
|
|
mComment['K'] = comment;
|
|
size_t position = 0;
|
|
size_t length = comment.length();
|
|
while (position < length)
|
|
{
|
|
std::size_t equals_position = comment.find('=', position);
|
|
if (equals_position != std::string::npos)
|
|
{
|
|
S8 type = comment.at(equals_position - 1);
|
|
position = comment.find('&', position);
|
|
if (position != std::string::npos)
|
|
{
|
|
mComment[type] = comment.substr(equals_position + 1, position - (equals_position + 1));
|
|
position++;
|
|
}
|
|
else
|
|
{
|
|
mComment[type] = comment.substr(equals_position + 1, length - (equals_position + 1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
position = equals_position;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool size_okay = true;
|
|
|
|
S32 discard_level = mRawDiscardLevel;
|
|
if (mRawDiscardLevel < 0)
|
|
{
|
|
LL_DEBUGS() << "Negative raw discard level when creating image: " << mRawDiscardLevel << LL_ENDL;
|
|
discard_level = 0;
|
|
}
|
|
|
|
U32 raw_width = mRawImage->getWidth() << discard_level;
|
|
U32 raw_height = mRawImage->getHeight() << discard_level;
|
|
|
|
if( raw_width > MAX_IMAGE_SIZE || raw_height > MAX_IMAGE_SIZE )
|
|
{
|
|
LL_INFOS() << "Width or height is greater than " << MAX_IMAGE_SIZE << ": (" << raw_width << "," << raw_height << ")" << LL_ENDL;
|
|
size_okay = false;
|
|
}
|
|
|
|
if (!LLImageGL::checkSize(mRawImage->getWidth(), mRawImage->getHeight()))
|
|
{
|
|
// A non power-of-two image was uploaded (through a non standard client)
|
|
LL_INFOS() << "Non power of two width or height: (" << mRawImage->getWidth() << "," << mRawImage->getHeight() << ")" << LL_ENDL;
|
|
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
|
|
LL_WARNS() << "!size_ok, setting as missing" << LL_ENDL;
|
|
setIsMissingAsset();
|
|
destroyRawImage();
|
|
return FALSE;
|
|
}
|
|
|
|
res = mGLTexturep->createGLTexture(mRawDiscardLevel, mRawImage, usename, TRUE, mBoostLevel);
|
|
|
|
notifyAboutCreatingTexture();
|
|
|
|
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(gSavedSettings,"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 = (F32) sqrt(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 == LLGLTexture::BOOST_UI || mBoostLevel == LLGLTexture::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 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
|
|
{
|
|
llassert(mNumFaces[ch] <= mFaceList[ch].size());
|
|
|
|
for(U32 i = 0; i < mNumFaces[ch]; i++)
|
|
{
|
|
LLFace* facep = mFaceList[ch][i];
|
|
if( facep )
|
|
{
|
|
LLDrawable* drawable = facep->getDrawable();
|
|
if (drawable)
|
|
{
|
|
if(drawable->isRecentlyVisible())
|
|
{
|
|
if (getBoostLevel() == LLViewerTexture::BOOST_NONE &&
|
|
drawable->getVObj() && drawable->getVObj()->isSelected())
|
|
{
|
|
setBoostLevel(LLViewerTexture::BOOST_SELECTED);
|
|
}
|
|
addTextureStats(facep->getVirtualSize());
|
|
setAdditionalDecodePriority(facep->getImportanceToCamera());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//reset whether or not a face was selected after 10 seconds
|
|
const F32 SELECTION_RESET_TIME = 10.f;
|
|
|
|
if (getBoostLevel() == LLViewerTexture::BOOST_SELECTED &&
|
|
gFrameTimeSeconds - mSelectedTime > SELECTION_RESET_TIME)
|
|
{
|
|
setBoostLevel(LLViewerTexture::BOOST_NONE);
|
|
}
|
|
|
|
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::setDebugFetching(S32 debug_level)
|
|
{
|
|
if(debug_level < 0)
|
|
{
|
|
mInDebug = FALSE;
|
|
return false;
|
|
}
|
|
mInDebug = TRUE;
|
|
|
|
mDesiredDiscardLevel = debug_level;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LLViewerFetchedTexture::isActiveFetching()
|
|
{
|
|
static LLCachedControl<bool> monitor_enabled(gSavedSettings,"DebugShowTextureInfo");
|
|
|
|
return mFetchState > 7 && mFetchState < 10 && monitor_enabled; //in state of WAIT_HTTP_REQ or DECODE_IMAGE.
|
|
}
|
|
|
|
bool LLViewerFetchedTexture::updateFetch()
|
|
{
|
|
static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled");
|
|
static LLCachedControl<F32> sCameraMotionThreshold(gSavedSettings,"TextureCameraMotionThreshold");
|
|
static LLCachedControl<S32> sCameraMotionBoost(gSavedSettings,"TextureCameraMotionBoost");
|
|
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(!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())
|
|
{
|
|
mHasAux = TRUE;
|
|
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())
|
|
{
|
|
#if 0
|
|
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
|
|
if (tester)
|
|
{
|
|
mIsFetched = TRUE;
|
|
tester->updateTextureLoadingStats(this, mRawImage, LLAppViewer::getTextureFetch()->isFromLocalCache(mID));
|
|
}
|
|
#endif
|
|
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();
|
|
LL_WARNS() << "oversize, setting as missing" << LL_ENDL;
|
|
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 (getDiscardLevel() < 0)
|
|
{
|
|
if (getFTType() != FTT_MAP_TILE)
|
|
{
|
|
LL_WARNS() << mID
|
|
<< " Fetch failure, setting as missing, decode_priority " << decode_priority
|
|
<< " mRawDiscardLevel " << mRawDiscardLevel
|
|
<< " current_discard " << current_discard
|
|
<< LL_ENDL;
|
|
}
|
|
setIsMissingAsset();
|
|
desired_discard = -1;
|
|
}
|
|
else
|
|
{
|
|
//LL_WARNS() << mID << ": Setting min discard to " << current_discard << LL_ENDL;
|
|
if(current_discard >= 0)
|
|
{
|
|
mMinDiscardLevel = current_discard;
|
|
desired_discard = current_discard;
|
|
}
|
|
else
|
|
{
|
|
S32 dis_level = getDiscardLevel();
|
|
mMinDiscardLevel = dis_level;
|
|
desired_discard = dis_level;
|
|
}
|
|
}
|
|
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)
|
|
// {
|
|
// LL_INFOS() << "Calling updateRequestPriority() with decode_priority = 0.0f" << LL_ENDL;
|
|
// 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(mDesiredDiscardLevel > getMaxDiscardLevel())
|
|
{
|
|
make_request = false;
|
|
}
|
|
else if (mNeedsCreateTexture || mIsMissingAsset)
|
|
{
|
|
make_request = false;
|
|
}
|
|
else if (current_discard >= 0 && current_discard <= mMinDiscardLevel)
|
|
{
|
|
make_request = false;
|
|
}
|
|
else if(mCachedRawImage.notNull() // can be empty
|
|
&& mCachedRawImageReady
|
|
&& (current_discard < 0 || current_discard > mCachedRawDiscardLevel))
|
|
{
|
|
make_request = false;
|
|
switchToCachedImage(); //use the cached raw data first
|
|
}
|
|
//else if (!isJustBound() && mCachedRawImageReady)
|
|
//{
|
|
// make_request = false;
|
|
//}
|
|
|
|
if (make_request)
|
|
{
|
|
// Load the texture progressively: we try not to rush to the desired discard too fast.
|
|
// If the camera is not moving, we do not tweak the discard level notch by notch but go to the desired discard with larger boosted steps
|
|
// This mitigates the "textures stay blurry" problem when loading while not killing the texture memory while moving around
|
|
S32 delta_level = (mBoostLevel > LLGLTexture::BOOST_NONE) ? 2 : 1;
|
|
if (current_discard < 0)
|
|
{
|
|
desired_discard = llmax(desired_discard, getMaxDiscardLevel() - delta_level);
|
|
}
|
|
else if (LLViewerTexture::sCameraMovingBias < sCameraMotionThreshold)
|
|
{
|
|
desired_discard = llmax(desired_discard, current_discard - sCameraMotionBoost);
|
|
}
|
|
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;
|
|
}
|
|
|
|
static LLCachedControl<U32> override_tex_discard_level(gSavedSettings, "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(mFTType, 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 received any network data
|
|
// for a while. Note - this is the normal mechanism for
|
|
// deleting requests, not just a place to handle timeouts.
|
|
const F32 FETCH_IDLE_TIME = 5.f;
|
|
if (mLastPacketTimer.getElapsedTimeF32() > FETCH_IDLE_TIME)
|
|
{
|
|
LL_DEBUGS("Texture") << "exceeded idle time " << FETCH_IDLE_TIME << ", deleting request: " << getID() << LL_ENDL;
|
|
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
|
|
mHasFetcher = FALSE;
|
|
}
|
|
}
|
|
|
|
llassert(mRawImage.notNull() || (!mNeedsCreateTexture && !mIsRawImageValid));
|
|
|
|
return mIsFetching ? true : false;
|
|
}
|
|
|
|
void LLViewerFetchedTexture::clearFetchedResults()
|
|
{
|
|
if(mNeedsCreateTexture || mIsFetching)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cleanup();
|
|
destroyGLTexture();
|
|
|
|
if(getDiscardLevel() >= 0) //sculpty texture, force to invalidate
|
|
{
|
|
mGLTexturep->forceToInvalidateGLTexture();
|
|
}
|
|
}
|
|
|
|
void LLViewerFetchedTexture::forceToDeleteRequest()
|
|
{
|
|
if (mHasFetcher)
|
|
{
|
|
//LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
|
|
mHasFetcher = FALSE;
|
|
mIsFetching = FALSE;
|
|
}
|
|
|
|
resetTextureStats();
|
|
|
|
mDesiredDiscardLevel = getMaxDiscardLevel() + 1; //defer LLAppViewer::getTextureFetch()->deleteRequest to updateFetch?
|
|
}
|
|
|
|
void LLViewerFetchedTexture::setIsMissingAsset(BOOL is_missing)
|
|
{
|
|
if (is_missing == mIsMissingAsset)
|
|
{
|
|
return;
|
|
}
|
|
if (is_missing)
|
|
{
|
|
notifyAboutMissingAsset();
|
|
|
|
if (mUrl.empty())
|
|
{
|
|
LL_WARNS() << mID << ": Marking image as missing" << LL_ENDL;
|
|
}
|
|
else
|
|
{
|
|
// This may or may not be an error - it is normal to have no
|
|
// map tile on an empty region, but bad if we're failing on a
|
|
// server bake texture.
|
|
if (getFTType() != FTT_MAP_TILE)
|
|
{
|
|
LL_WARNS() << mUrl << ": Marking image as missing" << LL_ENDL;
|
|
}
|
|
}
|
|
if (mHasFetcher)
|
|
{
|
|
LLAppViewer::getTextureFetch()->deleteRequest(getID(), true);
|
|
mHasFetcher = FALSE;
|
|
mIsFetching = FALSE;
|
|
mLastPacketTimer.reset();
|
|
mFetchState = 0;
|
|
mFetchPriority = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LL_INFOS() << mID << ": un-flagging missing asset" << LL_ENDL;
|
|
}
|
|
mIsMissingAsset = is_missing;
|
|
}
|
|
|
|
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)
|
|
{
|
|
if(!pause)
|
|
{
|
|
unpauseLoadedCallbacks(src_callback_list);
|
|
}
|
|
}
|
|
else if(pause)
|
|
{
|
|
pauseLoadedCallbacks(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)
|
|
{
|
|
if(mHasAux)
|
|
{
|
|
//trigger a refetch
|
|
forceToRefetchTexture();
|
|
}
|
|
else
|
|
{
|
|
// We need aux data, but we've already loaded the image, and it didn't have any
|
|
LL_WARNS() << "No aux data available for callback for image:" << getID() << LL_ENDL;
|
|
}
|
|
}
|
|
mLastCallBackActiveTime = sCurrentTime ;
|
|
mLastReferencedSavedRawImageTime = 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 != LLGLTexture::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 ;
|
|
mLastCallBackActiveTime = sCurrentTime ;
|
|
mForceCallbackFetch = TRUE;
|
|
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 = 900.f ; //seconds
|
|
static const F32 MAX_IDLE_WAIT_TIME = 5.f ; //seconds
|
|
|
|
if (mNeedsCreateTexture)
|
|
{
|
|
return false;
|
|
}
|
|
if(mPauseLoadedCallBacks)
|
|
{
|
|
destroyRawImage();
|
|
return false; //paused
|
|
}
|
|
if(sCurrentTime - mLastCallBackActiveTime > MAX_INACTIVE_TIME && !mIsFetching)
|
|
{
|
|
if (mFTType == FTT_SERVER_BAKE)
|
|
{
|
|
//output some debug info
|
|
LL_INFOS() << "baked texture: " << mID << "clears all call backs due to inactivity." << LL_ENDL;
|
|
LL_INFOS() << mUrl << LL_ENDL;
|
|
LL_INFOS() << "current discard: " << getDiscardLevel() << " current discard for fetch: " << getCurrentDiscardLevelForFetching() <<
|
|
" Desired discard: " << getDesiredDiscardLevel() << "decode Pri: " << getDecodePriority() << LL_ENDL;
|
|
}
|
|
|
|
clearCallbackEntryList() ; //remove all callbacks.
|
|
return false ;
|
|
}
|
|
|
|
bool res = false;
|
|
|
|
if (isMissingAsset())
|
|
{
|
|
if (mFTType == FTT_SERVER_BAKE)
|
|
{
|
|
//output some debug info
|
|
LL_INFOS() << "baked texture: " << mID << "is missing." << LL_ENDL;
|
|
LL_INFOS() << mUrl << LL_ENDL;
|
|
}
|
|
|
|
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);
|
|
return false;
|
|
}
|
|
|
|
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.
|
|
//LL_INFOS() << "doLoadedCallbacks raw for " << getID() << LL_ENDL;
|
|
|
|
// 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())
|
|
{
|
|
LL_WARNS() << "Raw Image with no Aux Data for callback" << LL_ENDL;
|
|
}
|
|
BOOL final = mRawDiscardLevel <= entryp->mDesiredDiscard ? TRUE : FALSE;
|
|
//LL_INFOS() << "Running callback for " << getID() << LL_ENDL;
|
|
//LL_INFOS() << mRawImage->getWidth() << "x" << mRawImage->getHeight() << LL_ENDL;
|
|
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()))
|
|
{
|
|
//LL_INFOS() << "doLoadedCallbacks GL for " << getID() << LL_ENDL;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Done with any raw image data at this point (will be re-created if we still have callbacks)
|
|
destroyRawImage();
|
|
|
|
//
|
|
// If we have no callbacks, take us off of the image callback list.
|
|
//
|
|
if (mLoadedCallbackList.empty())
|
|
{
|
|
gTextureList.mCallbackList.erase(this);
|
|
}
|
|
else if(!res && mForceCallbackFetch && sCurrentTime - mLastCallBackActiveTime > MAX_IDLE_WAIT_TIME && !mIsFetching)
|
|
{
|
|
//wait for long enough but no fetching request issued, force one.
|
|
forceToRefetchTexture(mLoadedCallbackDesiredDiscardLevel, 5.f);
|
|
mForceCallbackFetch = FALSE; //fire once.
|
|
}
|
|
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() && !needsToSaveRawImage())
|
|
{
|
|
sAuxCount--;
|
|
mAuxRawImage = NULL;
|
|
}
|
|
|
|
if (mRawImage.notNull())
|
|
{
|
|
sRawCount--;
|
|
|
|
if(mIsRawImageValid)
|
|
{
|
|
if(needsToSaveRawImage())
|
|
{
|
|
saveRawImage();
|
|
}
|
|
setCachedRawImage();
|
|
}
|
|
|
|
mRawImage = 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(LLGLTexture::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)
|
|
{
|
|
LL_WARNS() << "IMP-582: Trying to scale an image (" << mID << ") with 5 components!" << LL_ENDL;
|
|
mIsRawImageValid = 0;
|
|
return;
|
|
}
|
|
{
|
|
//make a duplicate in case somebody else is using this raw image
|
|
mRawImage = mRawImage->duplicate();
|
|
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())
|
|
{
|
|
LL_WARNS() << "mRawImage->getData() returns NULL" << LL_ENDL;
|
|
return;
|
|
}
|
|
|
|
mSavedRawDiscardLevel = mRawDiscardLevel;
|
|
mSavedRawImage = new LLImageRaw(mRawImage->getData(), mRawImage->getWidth(), mRawImage->getHeight(), mRawImage->getComponents());
|
|
|
|
if(mForceToSaveRawImage && mSavedRawDiscardLevel <= mDesiredSavedRawDiscardLevel)
|
|
{
|
|
mForceToSaveRawImage = FALSE;
|
|
}
|
|
|
|
mLastReferencedSavedRawImageTime = sCurrentTime;
|
|
}
|
|
|
|
//force to refetch the texture to the discard level
|
|
void LLViewerFetchedTexture::forceToRefetchTexture(S32 desired_discard, F32 kept_time)
|
|
{
|
|
if(mForceToSaveRawImage)
|
|
{
|
|
desired_discard = llmin(desired_discard, mDesiredSavedRawDiscardLevel);
|
|
kept_time = llmax(kept_time, mKeptSavedRawImageTime);
|
|
}
|
|
|
|
//trigger a new fetch.
|
|
mForceToSaveRawImage = TRUE ;
|
|
mDesiredSavedRawDiscardLevel = desired_discard ;
|
|
mKeptSavedRawImageTime = kept_time ;
|
|
mLastReferencedSavedRawImageTime = sCurrentTime ;
|
|
mSavedRawImage = NULL ;
|
|
mSavedRawDiscardLevel = -1 ;
|
|
}
|
|
|
|
void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard, F32 kept_time)
|
|
{
|
|
mKeptSavedRawImageTime = kept_time;
|
|
mLastReferencedSavedRawImageTime = sCurrentTime;
|
|
|
|
if(mSavedRawDiscardLevel > -1 && mSavedRawDiscardLevel <= desired_discard)
|
|
{
|
|
return; //raw imge is ready.
|
|
}
|
|
|
|
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()
|
|
{
|
|
if(mLastReferencedSavedRawImageTime < mKeptSavedRawImageTime)
|
|
{
|
|
return; //keep the saved raw image.
|
|
}
|
|
|
|
mForceToSaveRawImage = FALSE;
|
|
mSaveRawImage = FALSE;
|
|
|
|
clearCallbackEntryList();
|
|
|
|
mSavedRawImage = NULL ;
|
|
mForceToSaveRawImage = FALSE ;
|
|
mSaveRawImage = FALSE ;
|
|
mSavedRawDiscardLevel = -1 ;
|
|
mDesiredSavedRawDiscardLevel = -1 ;
|
|
mLastReferencedSavedRawImageTime = 0.0f ;
|
|
mKeptSavedRawImageTime = 0.f ;
|
|
|
|
if(mAuxRawImage.notNull())
|
|
{
|
|
sAuxCount--;
|
|
mAuxRawImage = NULL;
|
|
}
|
|
}
|
|
|
|
LLImageRaw* LLViewerFetchedTexture::getSavedRawImage()
|
|
{
|
|
mLastReferencedSavedRawImageTime = sCurrentTime;
|
|
|
|
return mSavedRawImage;
|
|
}
|
|
|
|
BOOL LLViewerFetchedTexture::hasSavedRawImage() const
|
|
{
|
|
return mSavedRawImage.notNull();
|
|
}
|
|
|
|
F32 LLViewerFetchedTexture::getElapsedLastReferencedSavedRawImageTime() const
|
|
{
|
|
return sCurrentTime - mLastReferencedSavedRawImageTime;
|
|
}
|
|
|
|
LLUUID LLViewerFetchedTexture::getUploader()
|
|
{
|
|
return (mComment.find('a') != mComment.end()) ? LLUUID(mComment['a']) : LLUUID::null;
|
|
}
|
|
|
|
LLDate LLViewerFetchedTexture::getUploadTime()
|
|
{
|
|
if (mComment.find('z') != mComment.end())
|
|
{
|
|
struct tm t = {0};
|
|
sscanf(mComment['z'].c_str(), "%4d%2d%2d%2d%2d%2d",
|
|
&t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
|
|
std::string iso_date = llformat("%d-%d-%dT%d:%d:%dZ", t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
|
|
return LLDate(iso_date);
|
|
}
|
|
return LLDate();
|
|
}
|
|
|
|
std::string LLViewerFetchedTexture::getComment()
|
|
{
|
|
return (mComment.find('K') != mComment.end()) ? mComment['K'] : LLStringUtil::null;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//end of LLViewerFetchedTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//start of LLViewerLODTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
LLViewerLODTexture::LLViewerLODTexture(const LLUUID& id, FTType f_type, const LLHost& host, BOOL usemipmaps)
|
|
: LLViewerFetchedTexture(id, f_type, host, usemipmaps)
|
|
{
|
|
init(TRUE);
|
|
}
|
|
|
|
LLViewerLODTexture::LLViewerLODTexture(const std::string& url, FTType f_type, const LLUUID& id, BOOL usemipmaps)
|
|
: LLViewerFetchedTexture(url, f_type, 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(gSavedSettings,"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 < LLGLTexture::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 < LLGLTexture::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 < LLGLTexture::BOOST_SCULPTED && current_discard >= 0)
|
|
{
|
|
// SH-2516 fix.
|
|
if(desired_discard_bias_max <= sDesiredDiscardBias && !mForceToSaveRawImage)
|
|
{
|
|
//needs to release texture memory urgently
|
|
scaleDown();
|
|
}
|
|
// Limit the amount of GL memory bound each frame
|
|
else if ( sBoundTextureMemory > sMaxBoundTextureMemory * texmem_middle_bound_scale &&
|
|
(!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
|
|
{
|
|
scaleDown();
|
|
}
|
|
// Only allow GL to have 2x the video card memory
|
|
else if ( sTotalTextureMemory > sMaxTotalTextureMem * texmem_middle_bound_scale &&
|
|
(!getBoundRecently() || mDesiredDiscardLevel >= mCachedRawDiscardLevel))
|
|
{
|
|
scaleDown();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0)
|
|
{
|
|
mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S8)mDesiredSavedRawDiscardLevel);
|
|
}
|
|
else if(LLPipeline::sMemAllocationThrottled)//release memory of large textures by decrease their resolutions.
|
|
{
|
|
if(scaleDown())
|
|
{
|
|
mDesiredDiscardLevel = mCachedRawDiscardLevel;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool LLViewerLODTexture::scaleDown()
|
|
{
|
|
if(hasGLTexture() && mCachedRawDiscardLevel > getDiscardLevel())
|
|
{
|
|
switchToCachedImage();
|
|
|
|
#if 0
|
|
LLTexturePipelineTester* tester = (LLTexturePipelineTester*)LLMetricPerformanceTesterBasic::getTester(sTesterName);
|
|
if (tester)
|
|
{
|
|
tester->setStablizingTime();
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//----------------------------------------------------------------------------------------------
|
|
//end of LLViewerLODTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------------------------------
|
|
//start of LLViewerMediaTexture
|
|
//----------------------------------------------------------------------------------------------
|
|
//static
|
|
void LLViewerMediaTexture::updateClass()
|
|
{
|
|
static const F32 MAX_INACTIVE_TIME = 30.f;
|
|
|
|
#if 0
|
|
//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->setAllowCompression(false);
|
|
|
|
mGLTexturep->setNeedsAlphaAndPickMask(FALSE);
|
|
|
|
mIsPlaying = FALSE;
|
|
|
|
setMediaImpl();
|
|
|
|
setCategory(LLGLTexture::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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
//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.
|
|
{
|
|
for (U32 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
|
|
{
|
|
const ll_face_list_t* face_list = tex->getFaceList(ch);
|
|
U32 end = tex->getNumFaces(ch);
|
|
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(LLRender::DIFFUSE_MAP, 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(LLRender::DIFFUSE_MAP, facep);
|
|
mIsPlaying = TRUE; //set the flag back.
|
|
|
|
if(getTotalNumFaces() < 1) //no face referencing to this media
|
|
{
|
|
stopPlaying();
|
|
}
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerMediaTexture::addFace(U32 ch, LLFace* facep)
|
|
{
|
|
LLViewerTexture::addFace(ch, 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
|
|
{
|
|
LL_ERRS() << "The face does not have a valid texture before media texture." << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
//virtual
|
|
void LLViewerMediaTexture::removeFace(U32 ch, LLFace* facep)
|
|
{
|
|
LLViewerTexture::removeFace(ch, 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;
|
|
}
|
|
}
|
|
|
|
std::vector<const LLTextureEntry*> te_list;
|
|
|
|
for (U32 ch = 0; ch < 3; ++ch)
|
|
{
|
|
//
|
|
//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.
|
|
|
|
llassert(mNumFaces[ch] <= mFaceList[ch].size());
|
|
|
|
for(U32 j = 0; j < mNumFaces[ch]; j++)
|
|
{
|
|
te_list.push_back(mFaceList[ch][j]->getTextureEntry());//all textures are in use.
|
|
}
|
|
}
|
|
|
|
if (te_list.empty())
|
|
{
|
|
mTextureList.clear();
|
|
return;
|
|
}
|
|
|
|
S32 end = te_list.size();
|
|
|
|
for(std::list< LLPointer<LLViewerTexture> >::iterator iter = mTextureList.begin();
|
|
iter != mTextureList.end(); ++iter)
|
|
{
|
|
S32 i = 0;
|
|
|
|
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
|
|
{
|
|
LL_ERRS() << "mTextureList texture reference number is corrupted." << LL_ENDL;
|
|
}
|
|
}
|
|
|
|
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(U32 ch, 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(ch, 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(ch, 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(LLRender::DIFFUSE_MAP, *iter);
|
|
}
|
|
}
|
|
else //stop playing this media
|
|
{
|
|
U32 ch = LLRender::DIFFUSE_MAP;
|
|
|
|
llassert(mNumFaces[ch] <= mFaceList[ch].size());
|
|
for(U32 i = mNumFaces[ch]; i; i--)
|
|
{
|
|
switchTexture(ch, mFaceList[ch][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 ch = 0; ch < LLRender::NUM_TEXTURE_CHANNELS; ++ch)
|
|
{
|
|
llassert(mNumFaces[ch] <= mFaceList[ch].size());
|
|
for(U32 i = 0; i < mNumFaces[ch]; i++)
|
|
{
|
|
LLFace* facep = mFaceList[ch][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;
|
|
}
|
|
//----------------------------------------------------------------------------------------------
|
|
//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 = (S32Bytes)0;
|
|
mTotalBytesLoadedFromCache = (S32Bytes)0;
|
|
mTotalBytesLoadedForLargeImage = (S32Bytes)0;
|
|
mTotalBytesLoadedForSculpties = (S32Bytes)0;
|
|
|
|
reset();
|
|
}
|
|
|
|
LLTexturePipelineTester::~LLTexturePipelineTester()
|
|
{
|
|
LLViewerTextureManager::sTesterp = NULL;
|
|
}
|
|
|
|
void LLTexturePipelineTester::update()
|
|
{
|
|
mLastTotalBytesUsed = mTotalBytesUsed;
|
|
mLastTotalBytesUsedForLargeImage = mTotalBytesUsedForLargeImage;
|
|
mTotalBytesUsed = (S32Bytes)0;
|
|
mTotalBytesUsedForLargeImage = (S32Bytes)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 = (S32Bytes)0;
|
|
mTotalBytesUsedForLargeImage = (S32Bytes)0;
|
|
mLastTotalBytesUsed = (S32Bytes)0;
|
|
mLastTotalBytesUsedForLargeImage = (S32Bytes)0;
|
|
|
|
mStartFetchingTime = 0.0f;
|
|
|
|
mTotalGrayTime = 0.0f;
|
|
mTotalStablizingTime = 0.0f;
|
|
|
|
mStartTimeLoadingSculpties = 1.0f;
|
|
mEndTimeLoadingSculpties = 0.0f;
|
|
}
|
|
|
|
//virtual
|
|
void LLTexturePipelineTester::outputTestRecord(LLSD *sd)
|
|
{
|
|
std::string currentLabel = getCurrentLabelName();
|
|
(*sd)[currentLabel]["TotalBytesLoaded"] = (LLSD::Integer)mTotalBytesLoaded.value();
|
|
(*sd)[currentLabel]["TotalBytesLoadedFromCache"] = (LLSD::Integer)mTotalBytesLoadedFromCache.value();
|
|
(*sd)[currentLabel]["TotalBytesLoadedForLargeImage"] = (LLSD::Integer)mTotalBytesLoadedForLargeImage.value();
|
|
(*sd)[currentLabel]["TotalBytesLoadedForSculpties"] = (LLSD::Integer)mTotalBytesLoadedForSculpties.value();
|
|
|
|
(*sd)[currentLabel]["StartFetchingTime"] = (LLSD::Real)mStartFetchingTime;
|
|
(*sd)[currentLabel]["TotalGrayTime"] = (LLSD::Real)mTotalGrayTime;
|
|
(*sd)[currentLabel]["TotalStablizingTime"] = (LLSD::Real)mTotalStablizingTime;
|
|
|
|
(*sd)[currentLabel]["StartTimeLoadingSculpties"] = (LLSD::Real)mStartTimeLoadingSculpties;
|
|
(*sd)[currentLabel]["EndTimeLoadingSculpties"] = (LLSD::Real)mEndTimeLoadingSculpties;
|
|
|
|
(*sd)[currentLabel]["Time"] = LLImageGL::sLastFrameTime;
|
|
(*sd)[currentLabel]["TotalBytesBound"] = (LLSD::Integer)mLastTotalBytesUsed.value();
|
|
(*sd)[currentLabel]["TotalBytesBoundForLargeImage"] = (LLSD::Integer)mLastTotalBytesUsedForLargeImage.value();
|
|
(*sd)[currentLabel]["PercentageBytesBound"] = (LLSD::Real)(100.f * mLastTotalBytesUsed / mTotalBytesLoaded);
|
|
}
|
|
|
|
void LLTexturePipelineTester::updateTextureBindingStats(const LLViewerTexture* imagep)
|
|
{
|
|
U32Bytes mem_size = imagep->getTextureMemory();
|
|
mTotalBytesUsed += mem_size;
|
|
|
|
if(MIN_LARGE_IMAGE_AREA <= (U32)(mem_size.value() / (U32)imagep->getComponents()))
|
|
{
|
|
mTotalBytesUsedForLargeImage += mem_size;
|
|
}
|
|
}
|
|
|
|
void LLTexturePipelineTester::updateTextureLoadingStats(const LLViewerFetchedTexture* imagep, const LLImageRaw* raw_imagep, BOOL from_cache)
|
|
{
|
|
U32Bytes data_size = (U32Bytes)raw_imagep->getDataSize();
|
|
mTotalBytesLoaded += data_size;
|
|
|
|
if(from_cache)
|
|
{
|
|
mTotalBytesLoadedFromCache += data_size;
|
|
}
|
|
|
|
if(MIN_LARGE_IMAGE_AREA <= (U32)(data_size.value() / (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)
|
|
{
|
|
LL_ERRS() << "type of test session does not match!" << LL_ENDL;
|
|
}
|
|
|
|
//compare and output the comparison
|
|
*os << llformat("%s\n", getTesterName().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
|
|
LLMetricPerformanceTesterWithSession::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
|
|
std::string currentLabel = getCurrentLabelName();
|
|
BOOL in_log = (*log).has(currentLabel);
|
|
while (in_log)
|
|
{
|
|
LLSD::String label = currentLabel;
|
|
|
|
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;
|
|
}
|
|
// Next label
|
|
incrementCurrentCount();
|
|
currentLabel = getCurrentLabelName();
|
|
in_log = (*log).has(currentLabel);
|
|
}
|
|
|
|
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
|
|
//----------------------------------------------------------------------------------------------
|
|
|