summaryrefslogtreecommitdiff
path: root/indra/llrender/llimagegl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/llimagegl.cpp')
-rw-r--r--indra/llrender/llimagegl.cpp943
1 files changed, 656 insertions, 287 deletions
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index e1231eeeb4..9d037f2565 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -2,31 +2,25 @@
* @file llimagegl.cpp
* @brief Generic GL image handler
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 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.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,10 +37,7 @@
#include "llmath.h"
#include "llgl.h"
#include "llrender.h"
-
-
//----------------------------------------------------------------------------
-
const F32 MIN_TEXTURE_LIFETIME = 10.f;
//statics
@@ -58,12 +49,38 @@ S32 LLImageGL::sGlobalTextureMemoryInBytes = 0;
S32 LLImageGL::sBoundTextureMemoryInBytes = 0;
S32 LLImageGL::sCurBoundTextureMemory = 0;
S32 LLImageGL::sCount = 0;
+std::list<U32> LLImageGL::sDeadTextureList;
BOOL LLImageGL::sGlobalUseAnisotropic = FALSE;
F32 LLImageGL::sLastFrameTime = 0.f;
+BOOL LLImageGL::sAllowReadBackRaw = FALSE ;
+LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
std::set<LLImageGL*> LLImageGL::sImageList;
+//****************************************************************************************************
+//The below for texture auditing use only
+//****************************************************************************************************
+//-----------------------
+//debug use
+BOOL gAuditTexture = FALSE ;
+#define MAX_TEXTURE_LOG_SIZE 22 //2048 * 2048
+std::vector<S32> LLImageGL::sTextureLoadedCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
+std::vector<S32> LLImageGL::sTextureBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
+std::vector<S32> LLImageGL::sTextureCurBoundCounter(MAX_TEXTURE_LOG_SIZE + 1) ;
+S32 LLImageGL::sCurTexSizeBar = -1 ;
+S32 LLImageGL::sCurTexPickSize = -1 ;
+LLPointer<LLImageGL> LLImageGL::sHighlightTexturep = NULL;
+S32 LLImageGL::sMaxCatagories = 1 ;
+
+std::vector<S32> LLImageGL::sTextureMemByCategory;
+std::vector<S32> LLImageGL::sTextureMemByCategoryBound ;
+std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ;
+//------------------------
+//****************************************************************************************************
+//End for texture auditing use only
+//****************************************************************************************************
+
//**************************************************************************************
//below are functions for debug use
//do not delete them even though they are not currently being used.
@@ -82,15 +99,24 @@ void check_all_images()
}
}
-void LLImageGL::checkTexSize() const
+void LLImageGL::checkTexSize(bool forced) const
{
- if (gDebugGL && mTarget == GL_TEXTURE_2D)
+ if ((forced || gDebugGL) && mTarget == GL_TEXTURE_2D)
{
+ {
+ //check viewport
+ GLint vp[4] ;
+ glGetIntegerv(GL_VIEWPORT, vp) ;
+ llcallstacks << "viewport: " << vp[0] << " : " << vp[1] << " : " << vp[2] << " : " << vp[3] << llcallstacksendl ;
+ }
+
GLint texname;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &texname);
BOOL error = FALSE;
if (texname != mTexName)
{
+ llinfos << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << llendl;
+
error = TRUE;
if (gDebugSession)
{
@@ -106,6 +132,8 @@ void LLImageGL::checkTexSize() const
glGetTexLevelParameteriv(mTarget, 0, GL_TEXTURE_WIDTH, (GLint*)&x);
glGetTexLevelParameteriv(mTarget, 0, GL_TEXTURE_HEIGHT, (GLint*)&y) ;
stop_glerror() ;
+ llcallstacks << "w: " << x << " h: " << y << llcallstacksendl ;
+
if(!x || !y)
{
return ;
@@ -115,11 +143,13 @@ void LLImageGL::checkTexSize() const
error = TRUE;
if (gDebugSession)
{
- gFailLog << "wrong texture size and discard level!" << std::endl;
+ gFailLog << "wrong texture size and discard level!" <<
+ mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << std::endl;
}
else
{
- llerrs << "wrong texture size and discard level!" << llendl ;
+ llerrs << "wrong texture size and discard level: width: " <<
+ mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << llendl ;
}
}
@@ -133,6 +163,60 @@ void LLImageGL::checkTexSize() const
//**************************************************************************************
//----------------------------------------------------------------------------
+BOOL is_little_endian()
+{
+ S32 a = 0x12345678;
+ U8 *c = (U8*)(&a);
+
+ return (*c == 0x78) ;
+}
+//static
+void LLImageGL::initClass(S32 num_catagories)
+{
+ sMaxCatagories = num_catagories ;
+
+ sTextureMemByCategory.resize(sMaxCatagories);
+ sTextureMemByCategoryBound.resize(sMaxCatagories) ;
+ sTextureCurMemByCategoryBound.resize(sMaxCatagories) ;
+}
+
+//static
+void LLImageGL::cleanupClass()
+{
+ sTextureMemByCategory.clear() ;
+ sTextureMemByCategoryBound.clear() ;
+ sTextureCurMemByCategoryBound.clear() ;
+}
+
+//static
+void LLImageGL::setHighlightTexture(S32 category)
+{
+ const S32 dim = 128;
+ sHighlightTexturep = new LLImageGL() ;
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3);
+ U8* data = image_raw->getData();
+ for (S32 i = 0; i<dim; i++)
+ {
+ for (S32 j = 0; j<dim; j++)
+ {
+ const S32 border = 2;
+ if (i<border || j<border || i>=(dim-border) || j>=(dim-border))
+ {
+ *data++ = 0xff;
+ *data++ = 0xff;
+ *data++ = 0xff;
+ }
+ else
+ {
+ *data++ = 0xff;
+ *data++ = 0xff;
+ *data++ = 0x00;
+ }
+ }
+ }
+ sHighlightTexturep->createGLTexture(0, image_raw, 0, TRUE, category);
+ image_raw = NULL;
+}
//static
S32 LLImageGL::dataFormatBits(S32 dataformat)
@@ -199,12 +283,32 @@ void LLImageGL::updateStats(F32 current_time)
sLastFrameTime = current_time;
sBoundTextureMemoryInBytes = sCurBoundTextureMemory;
sCurBoundTextureMemory = 0;
+
+ if(gAuditTexture)
+ {
+ for(U32 i = 0 ; i < sTextureCurBoundCounter.size() ; i++)
+ {
+ sTextureBoundCounter[i] = sTextureCurBoundCounter[i] ;
+ sTextureCurBoundCounter[i] = 0 ;
+ }
+ for(U32 i = 0 ; i < sTextureCurMemByCategoryBound.size() ; i++)
+ {
+ sTextureMemByCategoryBound[i] = sTextureCurMemByCategoryBound[i] ;
+ sTextureCurMemByCategoryBound[i] = 0 ;
+ }
+ }
}
//static
-S32 LLImageGL::updateBoundTexMem(const S32 delta)
+S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category)
{
- LLImageGL::sCurBoundTextureMemory += delta;
+ if(gAuditTexture && ncomponents > 0 && category > -1)
+ {
+ sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ;
+ sTextureCurMemByCategoryBound[category] += mem ;
+ }
+
+ LLImageGL::sCurBoundTextureMemory += mem ;
return LLImageGL::sCurBoundTextureMemory;
}
@@ -218,6 +322,7 @@ void LLImageGL::destroyGL(BOOL save_state)
gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE);
}
+ sAllowReadBackRaw = true ;
for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
iter != sImageList.end(); iter++)
{
@@ -227,7 +332,7 @@ void LLImageGL::destroyGL(BOOL save_state)
if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
{
glimage->mSaveData = new LLImageRaw;
- if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false))
+ if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
{
glimage->mSaveData = NULL ;
}
@@ -237,6 +342,7 @@ void LLImageGL::destroyGL(BOOL save_state)
stop_glerror();
}
}
+ sAllowReadBackRaw = false ;
}
//static
@@ -254,7 +360,7 @@ void LLImageGL::restoreGL()
{
if (glimage->getComponents() && glimage->mSaveData->getComponents())
{
- glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData);
+ glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory());
stop_glerror();
}
glimage->mSaveData = NULL; // deletes data
@@ -264,6 +370,7 @@ void LLImageGL::restoreGL()
//----------------------------------------------------------------------------
+//for server side use only.
//static
BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps)
{
@@ -271,12 +378,14 @@ BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps)
return TRUE;
}
+//for server side use only.
BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, U32 width, U32 height, U8 components, BOOL usemipmaps)
{
dest = new LLImageGL(width, height, components, usemipmaps);
return TRUE;
}
+//for server side use only.
BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps)
{
dest = new LLImageGL(imageraw, usemipmaps);
@@ -326,42 +435,58 @@ LLImageGL::~LLImageGL()
void LLImageGL::init(BOOL usemipmaps)
{
-#ifdef DEBUG_MISS
- mMissed = FALSE;
-#endif
+ // keep these members in the same order as declared in llimagehl.h
+ // so that it is obvious by visual inspection if we forgot to
+ // init a field.
+
+ mTextureMemory = 0;
+ mLastBindTime = 0.f;
- mPickMask = NULL;
- mTextureState = NO_DELETE ;
- mTextureMemory = 0;
- mLastBindTime = 0.f;
+ mPickMask = NULL;
+ mPickMaskWidth = 0;
+ mPickMaskHeight = 0;
+ mUseMipMaps = usemipmaps;
+ mHasExplicitFormat = FALSE;
+ mAutoGenMips = FALSE;
+
+ mIsMask = FALSE;
+ mNeedsAlphaAndPickMask = TRUE ;
+ mAlphaStride = 0 ;
+ mAlphaOffset = 0 ;
+
+ mGLTextureCreated = FALSE ;
+ mTexName = 0;
+ mWidth = 0;
+ mHeight = 0;
+ mCurrentDiscardLevel = -1;
+
+ mDiscardLevelInAtlas = -1 ;
+ mTexelsInAtlas = 0 ;
+ mTexelsInGLTexture = 0 ;
+
+ mTarget = GL_TEXTURE_2D;
+ mBindTarget = LLTexUnit::TT_TEXTURE;
+ mHasMipMaps = false;
+
+ mIsResident = 0;
- mTarget = GL_TEXTURE_2D;
- mBindTarget = LLTexUnit::TT_TEXTURE;
- mUseMipMaps = usemipmaps;
- mHasMipMaps = false;
- mAutoGenMips = FALSE;
- mTexName = 0;
- mIsResident = 0;
+ mComponents = 0;
+ mMaxDiscardLevel = MAX_DISCARD_LEVEL;
mTexOptionsDirty = true;
mAddressMode = LLTexUnit::TAM_WRAP;
mFilterOption = LLTexUnit::TFO_ANISOTROPIC;
- mWidth = 0;
- mHeight = 0;
- mComponents = 0;
-
- mMaxDiscardLevel = MAX_DISCARD_LEVEL;
- mCurrentDiscardLevel = -1;
- mDontDiscard = FALSE;
mFormatInternal = -1;
mFormatPrimary = (LLGLenum) 0;
mFormatType = GL_UNSIGNED_BYTE;
mFormatSwapBytes = FALSE;
- mHasExplicitFormat = FALSE;
- mGLTextureCreated = FALSE ;
- mIsMask = FALSE;
+#ifdef DEBUG_MISS
+ mMissed = FALSE;
+#endif
+
+ mCategory = -1;
}
void LLImageGL::cleanup()
@@ -403,7 +528,7 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents)
// Check if dimensions are a power of two!
if (!checkSize(width,height))
{
- llerrs << llformat("Texture has non power of two dimention: %dx%d",width,height) << llendl;
+ llerrs << llformat("Texture has non power of two dimension: %dx%d",width,height) << llendl;
}
if (mTexName)
@@ -411,7 +536,12 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents)
// llwarns << "Setting Size of LLImageGL with existing mTexName = " << mTexName << llendl;
destroyGLTexture();
}
-
+
+ // pickmask validity depends on old image size, delete it
+ delete [] mPickMask;
+ mPickMask = NULL;
+ mPickMaskWidth = mPickMaskHeight = 0;
+
mWidth = width;
mHeight = height;
mComponents = ncomponents;
@@ -461,8 +591,12 @@ void LLImageGL::dump()
}
//----------------------------------------------------------------------------
+void LLImageGL::forceUpdateBindStats(void) const
+{
+ mLastBindTime = sLastFrameTime;
+}
-void LLImageGL::updateBindStats(void) const
+BOOL LLImageGL::updateBindStats(S32 tex_mem) const
{
if (mTexName != 0)
{
@@ -474,38 +608,18 @@ void LLImageGL::updateBindStats(void) const
{
// we haven't accounted for this texture yet this frame
sUniqueCount++;
- updateBoundTexMem(mTextureMemory);
+ updateBoundTexMem(tex_mem, mComponents, mCategory);
mLastBindTime = sLastFrameTime;
- if(LLFastTimer::sMetricLog)
- {
- updateTestStats() ;
- }
+ return TRUE ;
}
}
+ return FALSE ;
}
-//virtual
-void LLImageGL::updateTestStats(void) const
-{
-}
-
-//virtual
-bool LLImageGL::bindError(const S32 stage) const
-{
- return false;
-}
-
-//virtual
-bool LLImageGL::bindDefaultImage(const S32 stage) const
-{
- return false;
-}
-
-//virtual
-void LLImageGL::forceImmediateUpdate()
+F32 LLImageGL::getTimePassedSinceLastBound()
{
- return ;
+ return sLastFrameTime - mLastBindTime ;
}
void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes )
@@ -520,6 +634,8 @@ void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_for
else
mFormatType = type_format;
mFormatSwapBytes = swap_bytes;
+
+ calcAlphaChannelOffsetAndStride() ;
}
//----------------------------------------------------------------------------
@@ -535,20 +651,16 @@ void LLImageGL::setImage(const LLImageRaw* imageraw)
void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
{
-// LLFastTimer t1(LLFastTimer::FTM_TEMP1);
- llpushcallstacks ;
bool is_compressed = false;
if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
{
is_compressed = true;
}
-// LLFastTimer t2(LLFastTimer::FTM_TEMP2);
- gGL.getTexUnit(0)->bind(this);
+ llverify(gGL.getTexUnit(0)->bind(this));
if (mUseMipMaps)
{
-// LLFastTimer t2(LLFastTimer::FTM_TEMP3);
if (data_hasmips)
{
// NOTE: data_in points to largest image; smaller images
@@ -565,14 +677,13 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
if (is_compressed)
{
-// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
glCompressedTexImage2DARB(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
stop_glerror();
}
else
{
-// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
+// LLFastTimer t2(FTM_TEMP4);
if(mFormatSwapBytes)
{
@@ -605,7 +716,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE);
stop_glerror();
{
-// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
+// LLFastTimer t2(FTM_TEMP4);
if(mFormatSwapBytes)
{
@@ -665,7 +776,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
llassert(w > 0 && h > 0 && cur_mip_data);
{
-// LLFastTimer t1(LLFastTimer::FTM_TEMP4);
+// LLFastTimer t1(FTM_TEMP4);
if(mFormatSwapBytes)
{
glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
@@ -748,12 +859,96 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
}
stop_glerror();
mGLTextureCreated = true;
- llpushcallstacks ;
}
-BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height)
+BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llwarns << "Trying to create a texture while GL is disabled!" << llendl;
+ return FALSE;
+ }
+ llassert(gGLManager.mInited);
+ stop_glerror();
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+ discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
+
+ // Actual image width/height = raw image width/height * 2^discard_level
+ S32 w = raw_image->getWidth() << discard_level;
+ S32 h = raw_image->getHeight() << discard_level;
+
+ // setSize may call destroyGLTexture if the size does not match
+ setSize(w, h, raw_image->getComponents());
+
+ if( !mHasExplicitFormat )
+ {
+ switch (mComponents)
+ {
+ case 1:
+ // Use luminance alpha (for fonts)
+ mFormatInternal = GL_LUMINANCE8;
+ mFormatPrimary = GL_LUMINANCE;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 2:
+ // Use luminance alpha (for fonts)
+ mFormatInternal = GL_LUMINANCE8_ALPHA8;
+ mFormatPrimary = GL_LUMINANCE_ALPHA;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 3:
+ mFormatInternal = GL_RGB8;
+ mFormatPrimary = GL_RGB;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 4:
+ mFormatInternal = GL_RGBA8;
+ mFormatPrimary = GL_RGBA;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl;
+ }
+ }
+
+ mCurrentDiscardLevel = discard_level;
+ mDiscardLevelInAtlas = discard_level;
+ mTexelsInAtlas = raw_image->getWidth() * raw_image->getHeight() ;
+ mLastBindTime = sLastFrameTime;
+ mGLTextureCreated = false ;
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, raw_image->getWidth());
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ return TRUE ;
+}
+
+void LLImageGL::postAddToAtlas()
+{
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ gGL.getTexUnit(0)->setTextureFilteringOption(mFilterOption);
+ stop_glerror();
+}
+
+BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
{
- llpushcallstacks ;
if (!width || !height)
{
return TRUE;
@@ -769,7 +964,8 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
return FALSE;
}
- if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height)
+ // HACK: allow the caller to explicitly force the fast path (i.e. using glTexSubImage2D here instead of calling setImage) even when updating the full texture.
+ if (!force_fast_update && x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height)
{
setImage(datap, FALSE);
}
@@ -842,20 +1038,27 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3
stop_glerror();
mGLTextureCreated = true;
}
- llpushcallstacks ;
return TRUE;
}
-BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height)
+BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update)
{
- return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height);
+ return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update);
}
// Copy sub image from frame buffer
BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height)
{
- if (gGL.getTexUnit(0)->bind(this, true))
+ if (gGL.getTexUnit(0)->bind(this, false, true))
{
+ if(gGLManager.mDebugGPU)
+ {
+ llinfos << "Calling glCopyTexSubImage2D(...)" << llendl ;
+ checkTexSize(true) ;
+ llcallstacks << fb_x << " : " << fb_y << " : " << x_pos << " : " << y_pos << " : " << width << " : " << height <<
+ " : " << (S32)mComponents << llcallstacksendl ;
+ }
+
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
mGLTextureCreated = true;
stop_glerror();
@@ -876,13 +1079,17 @@ void LLImageGL::generateTextures(S32 numTextures, U32 *textures)
// static
void LLImageGL::deleteTextures(S32 numTextures, U32 *textures)
{
- glDeleteTextures(numTextures, (GLuint*)textures);
+ for (S32 i = 0; i < numTextures; i++)
+ {
+ sDeadTextureList.push_back(textures[i]);
+ }
}
// static
void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels)
{
glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, pixels);
+ stop_glerror();
}
//create an empty GL texture: just create a texture name
@@ -915,14 +1122,14 @@ BOOL LLImageGL::createGLTexture()
return TRUE ;
}
-BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/)
+BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category)
{
- llpushcallstacks ;
if (gGLManager.mIsDisabled)
{
llwarns << "Trying to create a texture while GL is disabled!" << llendl;
return FALSE;
}
+
mGLTextureCreated = false ;
llassert(gGLManager.mInited);
stop_glerror();
@@ -935,8 +1142,10 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
// Actual image width/height = raw image width/height * 2^discard_level
- S32 w = imageraw->getWidth() << discard_level;
- S32 h = imageraw->getHeight() << discard_level;
+ S32 raw_w = imageraw->getWidth() ;
+ S32 raw_h = imageraw->getHeight() ;
+ S32 w = raw_w << discard_level;
+ S32 h = raw_h << discard_level;
// setSize may call destroyGLTexture if the size does not match
setSize(w, h, imageraw->getComponents());
@@ -970,15 +1179,25 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
default:
llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl;
}
+
+ calcAlphaChannelOffsetAndStride() ;
+ }
+
+ if(!to_create) //not create a gl texture
+ {
+ destroyGLTexture();
+ mCurrentDiscardLevel = discard_level;
+ mLastBindTime = sLastFrameTime;
+ return TRUE ;
}
+ setCategory(category) ;
const U8* rawdata = imageraw->getData();
return createGLTexture(discard_level, rawdata, FALSE, usename);
}
BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename)
{
- llpushcallstacks ;
llassert(data_in);
if (discard_level < 0)
@@ -1007,7 +1226,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
LLImageGL::generateTextures(1, &mTexName);
stop_glerror();
{
-// LLFastTimer t1(LLFastTimer::FTM_TEMP6);
llverify(gGL.getTexUnit(0)->bind(this));
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level);
@@ -1046,91 +1264,35 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_
if (old_name != 0)
{
sGlobalTextureMemoryInBytes -= mTextureMemory;
+
+ if(gAuditTexture)
+ {
+ decTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
+
LLImageGL::deleteTextures(1, &old_name);
+
stop_glerror();
}
mTextureMemory = getMipBytes(discard_level);
sGlobalTextureMemoryInBytes += mTextureMemory;
- setActive() ;
+ mTexelsInGLTexture = getWidth() * getHeight() ;
+ if(gAuditTexture)
+ {
+ incTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
// mark this as bound at this point, so we don't throw it out immediately
mLastBindTime = sLastFrameTime;
-
- llpushcallstacks ;
return TRUE;
}
-BOOL LLImageGL::setDiscardLevel(S32 discard_level)
-{
- llassert(discard_level >= 0);
- llassert(mCurrentDiscardLevel >= 0);
-
- discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
-
- if (mDontDiscard)
- {
- // don't discard!
- return FALSE;
- }
- else if (discard_level == mCurrentDiscardLevel)
- {
- // nothing to do
- return FALSE;
- }
- else if (discard_level < mCurrentDiscardLevel)
- {
- // larger image
- dump();
- llerrs << "LLImageGL::setDiscardLevel() called with larger discard level; use createGLTexture()" << llendl;
- return FALSE;
- }
- else if (mUseMipMaps)
- {
- LLPointer<LLImageRaw> imageraw = new LLImageRaw;
- while(discard_level > mCurrentDiscardLevel)
- {
- if (readBackRaw(discard_level, imageraw, false))
- {
- break;
- }
- discard_level--;
- }
- if (discard_level == mCurrentDiscardLevel)
- {
- // unable to increase the discard level
- return FALSE;
- }
- return createGLTexture(discard_level, imageraw);
- }
- else
- {
-#if !LL_LINUX && !LL_SOLARIS
- // *FIX: This should not be skipped for the linux client.
- llerrs << "LLImageGL::setDiscardLevel() called on image without mipmaps" << llendl;
-#endif
- return FALSE;
- }
-}
-
-BOOL LLImageGL::isValidForSculpt(S32 discard_level, S32 image_width, S32 image_height, S32 ncomponents)
-{
- assert_glerror();
- S32 gl_discard = discard_level - mCurrentDiscardLevel;
- LLGLint glwidth = 0;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth);
- LLGLint glheight = 0;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_HEIGHT, (GLint*)&glheight);
- LLGLint glcomponents = 0 ;
- glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&glcomponents);
- assert_glerror();
-
- return glwidth >= image_width && glheight >= image_height && (GL_RGB8 == glcomponents || GL_RGBA8 == glcomponents) ;
-}
-
BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const
{
- llpushcallstacks ;
+ llassert_always(sAllowReadBackRaw) ;
+ //llerrs << "should not call this function!" << llendl ;
+
if (discard_level < 0)
{
discard_level = mCurrentDiscardLevel;
@@ -1233,37 +1395,53 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
return FALSE ;
}
//-----------------------------------------------------------------------------------------------
- llpushcallstacks ;
+
return TRUE ;
}
-void LLImageGL::destroyGLTexture()
+void LLImageGL::deleteDeadTextures()
{
- if (mTexName != 0)
+ while (!sDeadTextureList.empty())
{
- stop_glerror();
-
+ GLuint tex = sDeadTextureList.front();
+ sDeadTextureList.pop_front();
for (int i = 0; i < gGLManager.mNumTextureUnits; i++)
{
- if (sCurrentBoundTextures[i] == mTexName)
+ if (sCurrentBoundTextures[i] == tex)
{
gGL.getTexUnit(i)->unbind(LLTexUnit::TT_TEXTURE);
stop_glerror();
}
}
-
- sGlobalTextureMemoryInBytes -= mTextureMemory;
- mTextureMemory = 0;
-
- LLImageGL::deleteTextures(1, &mTexName);
- mTextureState = DELETED ;
+
+ glDeleteTextures(1, &tex);
+ stop_glerror();
+ }
+}
+
+void LLImageGL::destroyGLTexture()
+{
+ if (mTexName != 0)
+ {
+ if(mTextureMemory)
+ {
+ if(gAuditTexture)
+ {
+ decTextureCounter(mTextureMemory, mComponents, mCategory) ;
+ }
+ sGlobalTextureMemoryInBytes -= mTextureMemory;
+ mTextureMemory = 0;
+ }
+
+ LLImageGL::deleteTextures(1, &mTexName);
mTexName = 0;
mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel.
mGLTextureCreated = FALSE ;
- stop_glerror();
}
}
+
+
//----------------------------------------------------------------------------
void LLImageGL::setAddressMode(LLTexUnit::eTextureAddressMode mode)
@@ -1289,12 +1467,12 @@ void LLImageGL::setFilteringOption(LLTexUnit::eTextureFilterOptions option)
mFilterOption = option;
}
- if (gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
+ if (mTexName != 0 && gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->getCurrTexture() == mTexName)
{
gGL.getTexUnit(gGL.getCurrentTexUnitIndex())->setTextureFilteringOption(option);
mTexOptionsDirty = false;
+ stop_glerror();
}
- stop_glerror();
}
BOOL LLImageGL::getIsResident(BOOL test_now)
@@ -1370,6 +1548,11 @@ S32 LLImageGL::getMipBytes(S32 discard_level) const
return res;
}
+BOOL LLImageGL::isJustBound() const
+{
+ return (BOOL)(sLastFrameTime - mLastBindTime < 0.5f);
+}
+
BOOL LLImageGL::getBoundRecently() const
{
return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME);
@@ -1381,137 +1564,222 @@ void LLImageGL::setTarget(const LLGLenum target, const LLTexUnit::eTextureType b
mBindTarget = bind_target;
}
-void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h)
+const S8 INVALID_OFFSET = -99 ;
+void LLImageGL::setNeedsAlphaAndPickMask(BOOL need_mask)
{
- if (mFormatType != GL_UNSIGNED_BYTE)
+ if(mNeedsAlphaAndPickMask != need_mask)
{
- llwarns << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << llendl;
+ mNeedsAlphaAndPickMask = need_mask;
+
+ if(mNeedsAlphaAndPickMask)
+ {
+ mAlphaOffset = 0 ;
+ }
+ else //do not need alpha mask
+ {
+ mAlphaOffset = INVALID_OFFSET ;
+ mIsMask = FALSE;
+ }
+ }
+}
+
+void LLImageGL::calcAlphaChannelOffsetAndStride()
+{
+ if(mAlphaOffset == INVALID_OFFSET)//do not need alpha mask
+ {
+ return ;
}
- U32 stride = 0;
+ mAlphaStride = -1 ;
switch (mFormatPrimary)
{
case GL_LUMINANCE:
case GL_ALPHA:
- stride = 1;
+ mAlphaStride = 1;
break;
case GL_LUMINANCE_ALPHA:
- stride = 2;
+ mAlphaStride = 2;
break;
case GL_RGB:
- //no alpha
+ mNeedsAlphaAndPickMask = FALSE ;
mIsMask = FALSE;
- return;
+ return ; //no alpha channel.
case GL_RGBA:
- stride = 4;
+ mAlphaStride = 4;
break;
case GL_BGRA_EXT:
- stride = 4;
+ mAlphaStride = 4;
break;
default:
- return;
+ break;
}
- U32 length = w * h;
- const GLubyte* current = ((const GLubyte*) data_in)+stride-1;
-
- S32 sample[16];
- memset(sample, 0, sizeof(S32)*16);
-
- for (U32 i = 0; i < length; i++)
+ mAlphaOffset = -1 ;
+ if (mFormatType == GL_UNSIGNED_BYTE)
{
- ++sample[*current/16];
- current += stride;
+ mAlphaOffset = mAlphaStride - 1 ;
}
-
- U32 total = 0;
- for (U32 i = 4; i < 11; i++)
+ else if(is_little_endian())
{
- total += sample[i];
- }
-
- if (total > length/16)
- {
- mIsMask = FALSE;
+ if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
+ {
+ mAlphaOffset = 0 ;
+ }
+ else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
+ {
+ mAlphaOffset = 3 ;
+ }
}
- else
+ else //big endian
{
- mIsMask = TRUE;
+ if (mFormatType == GL_UNSIGNED_INT_8_8_8_8)
+ {
+ mAlphaOffset = 3 ;
+ }
+ else if (mFormatType == GL_UNSIGNED_INT_8_8_8_8_REV)
+ {
+ mAlphaOffset = 0 ;
+ }
}
-}
-BOOL LLImageGL::isDeleted()
-{
- return mTextureState == DELETED ;
-}
+ if( mAlphaStride < 1 || //unsupported format
+ mAlphaOffset < 0 || //unsupported type
+ (mFormatPrimary == GL_BGRA_EXT && mFormatType != GL_UNSIGNED_BYTE)) //unknown situation
+ {
+ llwarns << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << llendl;
-BOOL LLImageGL::isInactive()
-{
- return mTextureState == INACTIVE ;
+ mNeedsAlphaAndPickMask = FALSE ;
+ mIsMask = FALSE;
+ }
}
-BOOL LLImageGL::isDeletionCandidate()
-{
- return mTextureState == DELETION_CANDIDATE ;
-}
+void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h)
+{
+ if(!mNeedsAlphaAndPickMask)
+ {
+ return ;
+ }
-void LLImageGL::setDeletionCandidate()
-{
- if(mTexName && (mTextureState == INACTIVE))
+ U32 length = w * h;
+ U32 alphatotal = 0;
+
+ U32 sample[16];
+ memset(sample, 0, sizeof(U32)*16);
+
+ // generate histogram of quantized alpha.
+ // also add-in the histogram of a 2x2 box-sampled version. The idea is
+ // this will mid-skew the data (and thus increase the chances of not
+ // being used as a mask) from high-frequency alpha maps which
+ // suffer the worst from aliasing when used as alpha masks.
+ if (w >= 2 && h >= 2)
+ {
+ llassert(w%2 == 0);
+ llassert(h%2 == 0);
+ const GLubyte* rowstart = ((const GLubyte*) data_in) + mAlphaOffset;
+ for (U32 y = 0; y < h; y+=2)
+ {
+ const GLubyte* current = rowstart;
+ for (U32 x = 0; x < w; x+=2)
+ {
+ const U32 s1 = current[0];
+ alphatotal += s1;
+ const U32 s2 = current[w * mAlphaStride];
+ alphatotal += s2;
+ current += mAlphaStride;
+ const U32 s3 = current[0];
+ alphatotal += s3;
+ const U32 s4 = current[w * mAlphaStride];
+ alphatotal += s4;
+ current += mAlphaStride;
+
+ ++sample[s1/16];
+ ++sample[s2/16];
+ ++sample[s3/16];
+ ++sample[s4/16];
+
+ const U32 asum = (s1+s2+s3+s4);
+ alphatotal += asum;
+ sample[asum/(16*4)] += 4;
+ }
+
+ rowstart += 2 * w * mAlphaStride;
+ }
+ length *= 2; // we sampled everything twice, essentially
+ }
+ else
{
- mTextureState = DELETION_CANDIDATE ;
+ const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset;
+ for (U32 i = 0; i < length; i++)
+ {
+ const U32 s1 = *current;
+ alphatotal += s1;
+ ++sample[s1/16];
+ current += mAlphaStride;
+ }
}
-}
+
+ // if more than 1/16th of alpha samples are mid-range, this
+ // shouldn't be treated as a 1-bit mask
-void LLImageGL::forceActive()
-{
- mTextureState = ACTIVE ;
-}
+ // also, if all of the alpha samples are clumped on one half
+ // of the range (but not at an absolute extreme), then consider
+ // this to be an intentional effect and don't treat as a mask.
-void LLImageGL::setActive()
-{
- if(mTextureState != NO_DELETE)
+ U32 midrangetotal = 0;
+ for (U32 i = 4; i < 11; i++)
{
- mTextureState = ACTIVE ;
+ midrangetotal += sample[i];
}
-}
-
-//set the texture inactive
-void LLImageGL::setInactive()
-{
- if(mTexName && (mTextureState == ACTIVE) && !getBoundRecently())
+ U32 lowerhalftotal = 0;
+ for (U32 i = 0; i < 8; i++)
{
- mTextureState = INACTIVE ;
+ lowerhalftotal += sample[i];
+ }
+ U32 upperhalftotal = 0;
+ for (U32 i = 8; i < 16; i++)
+ {
+ upperhalftotal += sample[i];
}
-}
-//set the texture to stay in memory
-void LLImageGL::setNoDelete()
-{
- mTextureState = NO_DELETE ;
+ if (midrangetotal > length/16 || // lots of midrange, or
+ (lowerhalftotal == length && alphatotal != 0) || // all close to transparent but not all totally transparent, or
+ (upperhalftotal == length && alphatotal != 255*length)) // all close to opaque but not all totally opaque
+ {
+ mIsMask = FALSE; // not suitable for masking
+ }
+ else
+ {
+ mIsMask = TRUE;
+ }
}
//----------------------------------------------------------------------------
void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in)
{
+ if(!mNeedsAlphaAndPickMask)
+ {
+ return ;
+ }
+
+ delete [] mPickMask;
+ mPickMask = NULL;
+ mPickMaskWidth = mPickMaskHeight = 0;
+
if (mFormatType != GL_UNSIGNED_BYTE ||
- mFormatPrimary != GL_RGBA)
+ mFormatPrimary != GL_RGBA)
{
//cannot generate a pick mask for this texture
- delete [] mPickMask;
- mPickMask = NULL;
return;
}
- U32 pick_width = width/2;
- U32 pick_height = height/2;
-
- U32 size = llmax(pick_width, (U32) 1) * llmax(pick_height, (U32) 1);
+ U32 pick_width = width/2 + 1;
+ U32 pick_height = height/2 + 1;
- size = size/8 + 1;
-
- delete[] mPickMask;
+ U32 size = pick_width * pick_height;
+ size = (size + 7) / 8; // pixelcount-to-bits
mPickMask = new U8[size];
+ mPickMaskWidth = pick_width - 1;
+ mPickMaskHeight = pick_height - 1;
memset(mPickMask, 0, sizeof(U8) * size);
@@ -1527,10 +1795,7 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in)
{
U32 pick_idx = pick_bit/8;
U32 pick_offset = pick_bit%8;
- if (pick_idx >= size)
- {
- llerrs << "WTF?" << llendl;
- }
+ llassert(pick_idx < size);
mPickMask[pick_idx] |= 1 << pick_offset;
}
@@ -1546,22 +1811,44 @@ BOOL LLImageGL::getMask(const LLVector2 &tc)
if (mPickMask)
{
- S32 width = getWidth()/2;
- S32 height = getHeight()/2;
+ F32 u,v;
+ if (LL_LIKELY(tc.isFinite()))
+ {
+ u = tc.mV[0] - floorf(tc.mV[0]);
+ v = tc.mV[1] - floorf(tc.mV[1]);
+ }
+ else
+ {
+ LL_WARNS_ONCE("render") << "Ugh, non-finite u/v in mask pick" << LL_ENDL;
+ u = v = 0.f;
+ // removing assert per EXT-4388
+ // llassert(false);
+ }
+
+ if (LL_UNLIKELY(u < 0.f || u > 1.f ||
+ v < 0.f || v > 1.f))
+ {
+ LL_WARNS_ONCE("render") << "Ugh, u/v out of range in image mask pick" << LL_ENDL;
+ u = v = 0.f;
+ // removing assert per EXT-4388
+ // llassert(false);
+ }
- F32 u = tc.mV[0] - floorf(tc.mV[0]);
- F32 v = tc.mV[1] - floorf(tc.mV[1]);
+ S32 x = llfloor(u * mPickMaskWidth);
+ S32 y = llfloor(v * mPickMaskHeight);
- if (u < 0.f || u > 1.f ||
- v < 0.f || v > 1.f)
+ if (LL_UNLIKELY(x > mPickMaskWidth))
{
- llerrs << "WTF?" << llendl;
+ LL_WARNS_ONCE("render") << "Ooh, width overrun on pick mask read, that coulda been bad." << LL_ENDL;
+ x = llmax((U16)0, mPickMaskWidth);
+ }
+ if (LL_UNLIKELY(y > mPickMaskHeight))
+ {
+ LL_WARNS_ONCE("render") << "Ooh, height overrun on pick mask read, that woulda been bad." << LL_ENDL;
+ y = llmax((U16)0, mPickMaskHeight);
}
-
- S32 x = (S32)(u * width);
- S32 y = (S32)(v * height);
- S32 idx = y*width+x;
+ S32 idx = y*mPickMaskWidth+x;
S32 offset = idx%8;
res = mPickMask[idx/8] & (1 << offset) ? TRUE : FALSE;
@@ -1570,6 +1857,88 @@ BOOL LLImageGL::getMask(const LLVector2 &tc)
return res;
}
+void LLImageGL::setCategory(S32 category)
+{
+ if(!gAuditTexture)
+ {
+ return ;
+ }
+ if(mCategory != category)
+ {
+ if(mCategory > -1)
+ {
+ sTextureMemByCategory[mCategory] -= mTextureMemory ;
+ }
+ if(category > -1 && category < sMaxCatagories)
+ {
+ sTextureMemByCategory[category] += mTextureMemory ;
+ mCategory = category;
+ }
+ else
+ {
+ mCategory = -1 ;
+ }
+ }
+}
+
+//for debug use
+//val is a "power of two" number
+S32 LLImageGL::getTextureCounterIndex(U32 val)
+{
+ //index range is [0, MAX_TEXTURE_LOG_SIZE].
+ if(val < 2)
+ {
+ return 0 ;
+ }
+ else if(val >= (1 << MAX_TEXTURE_LOG_SIZE))
+ {
+ return MAX_TEXTURE_LOG_SIZE ;
+ }
+ else
+ {
+ S32 ret = 0 ;
+ while(val >>= 1)
+ {
+ ++ret;
+ }
+ return ret ;
+ }
+}
+
+//static
+void LLImageGL::incTextureCounter(U32 val, S32 ncomponents, S32 category)
+{
+ sTextureLoadedCounter[getTextureCounterIndex(val)]++ ;
+ sTextureMemByCategory[category] += (S32)val * ncomponents ;
+}
+
+//static
+void LLImageGL::decTextureCounter(U32 val, S32 ncomponents, S32 category)
+{
+ sTextureLoadedCounter[getTextureCounterIndex(val)]-- ;
+ sTextureMemByCategory[category] += (S32)val * ncomponents ;
+}
+
+void LLImageGL::setCurTexSizebar(S32 index, BOOL set_pick_size)
+{
+ sCurTexSizeBar = index ;
+
+ if(set_pick_size)
+ {
+ sCurTexPickSize = (1 << index) ;
+ }
+ else
+ {
+ sCurTexPickSize = -1 ;
+ }
+}
+void LLImageGL::resetCurTexSizebar()
+{
+ sCurTexSizeBar = -1 ;
+ sCurTexPickSize = -1 ;
+}
+//----------------------------------------------------------------------------
+
//----------------------------------------------------------------------------