Skip to content
Snippets Groups Projects
llimagegl.cpp 58.2 KiB
Newer Older
James Cook's avatar
James Cook committed
/** 
 * @file llimagegl.cpp
 * @brief Generic GL image handler
 *
 * $LicenseInfo:firstyear=2001&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$
James Cook's avatar
James Cook committed
 */


// TODO: create 2 classes for images w/ and w/o discard levels?

#include "linden_common.h"

#include "llimagegl.h"

#include "llerror.h"
#include "llfasttimer.h"
James Cook's avatar
James Cook committed
#include "llimage.h"

#include "llmath.h"
#include "llgl.h"
#include "llrender.h"
James Cook's avatar
James Cook committed
//----------------------------------------------------------------------------
const F32 MIN_TEXTURE_LIFETIME = 10.f;

//which power of 2 is i?
//assumes i is a power of 2 > 0
U32 wpo2(U32 i);

James Cook's avatar
James Cook committed
//statics

U32 LLImageGL::sUniqueCount				= 0;
U32 LLImageGL::sBindCount				= 0;
S64Bytes LLImageGL::sGlobalTextureMemory(0);
S64Bytes LLImageGL::sBoundTextureMemory(0);
S64Bytes LLImageGL::sCurBoundTextureMemory(0);
James Cook's avatar
James Cook committed
S32 LLImageGL::sCount					= 0;

BOOL LLImageGL::sGlobalUseAnisotropic	= FALSE;
F32 LLImageGL::sLastFrameTime			= 0.f;
BOOL LLImageGL::sAllowReadBackRaw       = FALSE ;
LLImageGL* LLImageGL::sDefaultGLTexture = NULL ;
bool LLImageGL::sCompressTextures = false;
James Cook's avatar
James Cook committed
std::set<LLImageGL*> LLImageGL::sImageList;
//****************************************************************************************************
//The below for texture auditing use only
//****************************************************************************************************
//-----------------------
//debug use
S32 LLImageGL::sCurTexSizeBar = -1 ;
S32 LLImageGL::sCurTexPickSize = -1 ;
//optimization for when we don't need to calculate mIsMask
BOOL LLImageGL::sSkipAnalyzeAlpha;

//------------------------
//****************************************************************************************************
//End for texture auditing use only
//****************************************************************************************************

//**************************************************************************************
//below are functions for debug use
//do not delete them even though they are not currently being used.
void check_all_images()
{
	for (std::set<LLImageGL*>::iterator iter = LLImageGL::sImageList.begin();
		 iter != LLImageGL::sImageList.end(); iter++)
	{
		LLImageGL* glimage = *iter;
		if (glimage->getTexName() && glimage->isGLTextureCreated())
		{
			gGL.getTexUnit(0)->bind(glimage) ;
			glimage->checkTexSize() ;
			gGL.getTexUnit(0)->unbind(glimage->getTarget()) ;
		}
	}
}

void LLImageGL::checkTexSize(bool forced) const
	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);
			LL_INFOS() << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << LL_ENDL;
			error = TRUE;
			if (gDebugSession)
			{
				gFailLog << "Invalid texture bound!" << std::endl;
			}
			else
			{
				LL_ERRS() << "Invalid texture bound!" << LL_ENDL;
		}
		stop_glerror() ;
		LLGLint x = 0, y = 0 ;
		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 ;
		}
		if(x != (mWidth >> mCurrentDiscardLevel) || y != (mHeight >> mCurrentDiscardLevel))
		{
			error = TRUE;
			if (gDebugSession)
			{
				gFailLog << "wrong texture size and discard level!" << 
					mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << std::endl;
				LL_ERRS() << "wrong texture size and discard level: width: " << 
					mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << LL_ENDL ;
			}
		}

		if (error)
		{
			ll_fail("LLImageGL::checkTexSize failed.");
		}
	}
}
//end of debug functions
//**************************************************************************************

James Cook's avatar
James Cook committed
//----------------------------------------------------------------------------
BOOL is_little_endian()
{
	S32 a = 0x12345678;
    U8 *c = (U8*)(&a);
    
	return (*c == 0x78) ;
}
//static 
void LLImageGL::initClass(S32 num_catagories, BOOL skip_analyze_alpha /* = false */)
	sSkipAnalyzeAlpha = skip_analyze_alpha;
James Cook's avatar
James Cook committed

//static
S32 LLImageGL::dataFormatBits(S32 dataformat)
{
    switch (dataformat)
    {
    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:	        return 4;
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:    return 4;
    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:	        return 8;
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:    return 8;
    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:	        return 8;
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:    return 8;
    case GL_LUMINANCE:						        return 8;
    case GL_ALPHA:							        return 8;
    case GL_COLOR_INDEX:						    return 8;
    case GL_LUMINANCE_ALPHA:					    return 16;
    case GL_RGB:								    return 24;
    case GL_SRGB:								    return 24;
    case GL_RGB8:								    return 24;
    case GL_RGBA:								    return 32;
    case GL_SRGB_ALPHA:						        return 32;
    case GL_BGRA:								    return 32;		// Used for QuickTime media textures on the Mac
    default:
        LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
        return 0;
    }
James Cook's avatar
James Cook committed
}

//static
S32 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height)
{
    switch (dataformat)
    {
    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
        if (width < 4) width = 4;
        if (height < 4) height = 4;
        break;
    default:
        break;
    }
James Cook's avatar
James Cook committed
	S32 bytes ((width*height*dataFormatBits(dataformat)+7)>>3);
	S32 aligned = (bytes+3)&~3;
	return aligned;
}

//static
S32 LLImageGL::dataFormatComponents(S32 dataformat)
{
	switch (dataformat)
	{
	  case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:	return 3;
	  case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: return 3;
James Cook's avatar
James Cook committed
	  case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:	return 4;
	  case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: return 4;
James Cook's avatar
James Cook committed
	  case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:	return 4;
	  case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: return 4;
James Cook's avatar
James Cook committed
	  case GL_LUMINANCE:						return 1;
	  case GL_ALPHA:							return 1;
	  case GL_COLOR_INDEX:						return 1;
	  case GL_LUMINANCE_ALPHA:					return 2;
	  case GL_RGB:								return 3;
	  case GL_SRGB:								return 3;
James Cook's avatar
James Cook committed
	  case GL_RGBA:								return 4;
	  case GL_SRGB_ALPHA:						return 4;
James Cook's avatar
James Cook committed
	  case GL_BGRA:								return 4;		// Used for QuickTime media textures on the Mac
	  default:
		LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL;
James Cook's avatar
James Cook committed
		return 0;
	}
}

//----------------------------------------------------------------------------

static LLTrace::BlockTimerStatHandle FTM_IMAGE_UPDATE_STATS("Image Stats");
James Cook's avatar
James Cook committed
// static
void LLImageGL::updateStats(F32 current_time)
{
	LL_RECORD_BLOCK_TIME(FTM_IMAGE_UPDATE_STATS);
James Cook's avatar
James Cook committed
	sLastFrameTime = current_time;
	sBoundTextureMemory = sCurBoundTextureMemory;
	sCurBoundTextureMemory = S64Bytes(0);
James Cook's avatar
James Cook committed
}

//static
S64 LLImageGL::updateBoundTexMem(const S64Bytes mem, const S32 ncomponents, S32 category)
James Cook's avatar
James Cook committed
{
	LLImageGL::sCurBoundTextureMemory += mem ;
	return LLImageGL::sCurBoundTextureMemory.value();
James Cook's avatar
James Cook committed
}

//----------------------------------------------------------------------------

//static 
void LLImageGL::destroyGL(BOOL save_state)
{
	for (S32 stage = 0; stage < gGLManager.mNumTextureUnits; stage++)
	{
		gGL.getTexUnit(stage)->unbind(LLTexUnit::TT_TEXTURE);
James Cook's avatar
James Cook committed
	}
	sAllowReadBackRaw = true ;
James Cook's avatar
James Cook committed
	for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
		 iter != sImageList.end(); iter++)
	{
		LLImageGL* glimage = *iter;
James Cook's avatar
James Cook committed
		{
			if (save_state && glimage->isGLTextureCreated() && glimage->mComponents)
James Cook's avatar
James Cook committed
			{
				glimage->mSaveData = new LLImageRaw;
				glimage->claimMem(glimage->mSaveData);
				if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it.
					glimage->disclaimMem(glimage->mSaveData);
James Cook's avatar
James Cook committed
			}
James Cook's avatar
James Cook committed
			glimage->destroyGLTexture();
			stop_glerror();
		}
	}
	sAllowReadBackRaw = false ;
James Cook's avatar
James Cook committed
}

//static 
void LLImageGL::restoreGL()
{
	for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
		 iter != sImageList.end(); iter++)
	{
		LLImageGL* glimage = *iter;
			LL_ERRS() << "tex name is not 0." << LL_ENDL ;
James Cook's avatar
James Cook committed
		{
			if (glimage->getComponents() && glimage->mSaveData->getComponents())
James Cook's avatar
James Cook committed
			{
				glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData, 0, TRUE, glimage->getCategory());
James Cook's avatar
James Cook committed
				stop_glerror();
			}
			glimage->mSaveData = NULL; // deletes data
		}
	}
}

//static 
void LLImageGL::dirtyTexOptions()
{
	for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
		 iter != sImageList.end(); iter++)
	{
		LLImageGL* glimage = *iter;
		glimage->mTexOptionsDirty = true;
		stop_glerror();
	}
	
}
James Cook's avatar
James Cook committed
//----------------------------------------------------------------------------

//for server side use only.
//static 
BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps)
{
	dest = new LLImageGL(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);
	return TRUE;
}

//----------------------------------------------------------------------------

James Cook's avatar
James Cook committed
LLImageGL::LLImageGL(BOOL usemipmaps)
:	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
    mSaveData(0), mExternalTexture(FALSE)
James Cook's avatar
James Cook committed
{
	init(usemipmaps);
	setSize(0, 0, 0);
	sImageList.insert(this);
	sCount++;
}

LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps)
:	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
    mSaveData(0), mExternalTexture(FALSE)
James Cook's avatar
James Cook committed
{
	llassert( components <= 4 );
	init(usemipmaps);
	setSize(width, height, components);
	sImageList.insert(this);
	sCount++;
}

LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps)
:	LLTrace::MemTrackable<LLImageGL>("LLImageGL"),
    mSaveData(0), mExternalTexture(FALSE)
James Cook's avatar
James Cook committed
{
	init(usemipmaps);
	setSize(0, 0, 0);
	sImageList.insert(this);
	sCount++;
James Cook's avatar
James Cook committed
	createGLTexture(0, imageraw); 
}

LLImageGL::LLImageGL(
    LLGLuint texName,
    U32 components,
    LLGLenum target,
    LLGLint  formatInternal,
    LLGLenum formatPrimary,
    LLGLenum formatType,
    LLTexUnit::eTextureAddressMode addressMode)
    : LLTrace::MemTrackable<LLImageGL>("LLImageGL"), mSaveData(0), mExternalTexture(TRUE)
{
    init(false);
    mTexName = texName;
    mTarget = target;
    mComponents = components;
    mAddressMode = addressMode;
    mFormatType = formatType;
    mFormatInternal = formatInternal;
    mFormatPrimary = formatPrimary;
}


James Cook's avatar
James Cook committed
LLImageGL::~LLImageGL()
{
    if (!mExternalTexture)
    {
	    LLImageGL::cleanup();
	    sImageList.erase(this);
	    freePickMask();
	    sCount--;
    }
const S8 INVALID_OFFSET = -99 ;

James Cook's avatar
James Cook committed
void LLImageGL::init(BOOL usemipmaps)
{
	// 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.

	mPickMaskWidth = 0;
	mPickMaskHeight = 0;
	mUseMipMaps = usemipmaps;
	mHasExplicitFormat = FALSE;
	mAutoGenMips = FALSE;

	mIsMask = FALSE;
	mMaskRMSE = 1.f ;
	mMaskMidPercentile = 1.f;

	mNeedsAlphaAndPickMask = FALSE ;
	mAlphaOffset = INVALID_OFFSET ;

	mGLTextureCreated = FALSE ;
	mTexName = 0;
	mWidth = 0;
	mHeight	= 0;
	mCurrentDiscardLevel = -1;	
James Cook's avatar
James Cook committed

	mDiscardLevelInAtlas = -1 ;
	mTexelsInAtlas = 0 ;
	mTexelsInGLTexture = 0 ;
	mTarget = GL_TEXTURE_2D;
	mBindTarget = LLTexUnit::TT_TEXTURE;
	mHasMipMaps = false;

	mIsResident = 0;

	mComponents = 0;
	mMaxDiscardLevel = MAX_DISCARD_LEVEL;

	mTexOptionsDirty = true;
	mAddressMode = LLTexUnit::TAM_WRAP;
	mFilterOption = LLTexUnit::TFO_ANISOTROPIC;
James Cook's avatar
James Cook committed
	
	mFormatInternal = -1;
	mFormatPrimary = (LLGLenum) 0;
	mFormatType = GL_UNSIGNED_BYTE;
	mFormatSwapBytes = FALSE;
James Cook's avatar
James Cook committed
}

void LLImageGL::cleanup()
{
	if (!gGLManager.mIsDisabled)
	{
		destroyGLTexture();
	}
James Cook's avatar
James Cook committed
	mSaveData = NULL; // deletes data
}

//----------------------------------------------------------------------------

//this function is used to check the size of a texture image.
//so dim should be a positive number
James Cook's avatar
James Cook committed
static bool check_power_of_two(S32 dim)
{
James Cook's avatar
James Cook committed
	{
		return false ;
	}
	if(!dim)//0 is a power-of-two number
	{
		return true ;
James Cook's avatar
James Cook committed
	}
	return !(dim & (dim - 1)) ;
James Cook's avatar
James Cook committed
}

//static
bool LLImageGL::checkSize(S32 width, S32 height)
{
	return check_power_of_two(width) && check_power_of_two(height);
}

bool LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level)
James Cook's avatar
James Cook committed
{
	if (width != mWidth || height != mHeight || ncomponents != mComponents)
	{
		// Check if dimensions are a power of two!
		if (!checkSize(width,height))
		{
			LL_WARNS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL;
James Cook's avatar
James Cook committed
		}
		
		if (mTexName)
		{
// 			LL_WARNS() << "Setting Size of LLImageGL with existing mTexName = " << mTexName << LL_ENDL;
James Cook's avatar
James Cook committed
			destroyGLTexture();
		}

		// pickmask validity depends on old image size, delete it
James Cook's avatar
James Cook committed
		mWidth = width;
		mHeight = height;
		mComponents = ncomponents;
		if (ncomponents > 0)
		{
			mMaxDiscardLevel = 0;
			while (width > 1 && height > 1 && mMaxDiscardLevel < MAX_DISCARD_LEVEL)
			{
				mMaxDiscardLevel++;
				width >>= 1;
				height >>= 1;
			}

			if(discard_level > 0)
			{
				mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level);
			}
James Cook's avatar
James Cook committed
		}
		else
		{
			mMaxDiscardLevel = MAX_DISCARD_LEVEL;
		}
	}
James Cook's avatar
James Cook committed
}

//----------------------------------------------------------------------------

// virtual
void LLImageGL::dump()
{
	LL_INFOS() << "mMaxDiscardLevel " << S32(mMaxDiscardLevel)
James Cook's avatar
James Cook committed
			<< " mLastBindTime " << mLastBindTime
			<< " mTarget " << S32(mTarget)
			<< " mBindTarget " << S32(mBindTarget)
			<< " mUseMipMaps " << S32(mUseMipMaps)
			<< " mHasMipMaps " << S32(mHasMipMaps)
			<< " mCurrentDiscardLevel " << S32(mCurrentDiscardLevel)
			<< " mFormatInternal " << S32(mFormatInternal)
			<< " mFormatPrimary " << S32(mFormatPrimary)
			<< " mFormatType " << S32(mFormatType)
			<< " mFormatSwapBytes " << S32(mFormatSwapBytes)
			<< " mHasExplicitFormat " << S32(mHasExplicitFormat)
#if DEBUG_MISS
			<< " mMissed " << mMissed
#endif
			<< LL_ENDL;
James Cook's avatar
James Cook committed

	LL_INFOS() << " mTextureMemory " << mTextureMemory
James Cook's avatar
James Cook committed
			<< " mTexNames " << mTexName
			<< " mIsResident " << S32(mIsResident)
			<< LL_ENDL;
James Cook's avatar
James Cook committed
}

//----------------------------------------------------------------------------
void LLImageGL::forceUpdateBindStats(void) const
{
	mLastBindTime = sLastFrameTime;
}
James Cook's avatar
James Cook committed

BOOL LLImageGL::updateBindStats(S64Bytes tex_mem) const
James Cook's avatar
James Cook committed
	if (mTexName != 0)
	{
#ifdef DEBUG_MISS
		mMissed = ! getIsResident(TRUE);
#endif
James Cook's avatar
James Cook committed
		if (mLastBindTime != sLastFrameTime)
		{
			// we haven't accounted for this texture yet this frame
			updateBoundTexMem(tex_mem, mComponents, mCategory);
James Cook's avatar
James Cook committed
			mLastBindTime = sLastFrameTime;
			return TRUE ;
James Cook's avatar
James Cook committed
		}
	}
	return FALSE ;
F32 LLImageGL::getTimePassedSinceLastBound()
	return sLastFrameTime - mLastBindTime ;
James Cook's avatar
James Cook committed
void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes )
{
	// Note: must be called before createTexture()
	// Note: it's up to the caller to ensure that the format matches the number of components.
	mHasExplicitFormat = TRUE;
	mFormatInternal = internal_format;
	mFormatPrimary = primary_format;
	if(type_format == 0)
		mFormatType = GL_UNSIGNED_BYTE;
	else
		mFormatType = type_format;
	mFormatSwapBytes = swap_bytes;

	calcAlphaChannelOffsetAndStride() ;
James Cook's avatar
James Cook committed
}

//----------------------------------------------------------------------------

void LLImageGL::setImage(const LLImageRaw* imageraw)
{
	llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
			 (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
			 (imageraw->getComponents() == getComponents()));
	const U8* rawdata = imageraw->getData();
	setImage(rawdata, FALSE);
}

static LLTrace::BlockTimerStatHandle FTM_SET_IMAGE("setImage");
BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
James Cook's avatar
James Cook committed
{
	LL_RECORD_BLOCK_TIME(FTM_SET_IMAGE);
James Cook's avatar
James Cook committed
	bool is_compressed = false;
    switch (mFormatPrimary)
    {
    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
    case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
        is_compressed = true;
        break;
    default:
        break;
    }
	
	
	if (mUseMipMaps)
	{
		//set has mip maps to true before binding image so tex parameters get set properly
		gGL.getTexUnit(0)->unbind(mBindTarget);
		mHasMipMaps = true;
		mTexOptionsDirty = true;
		setFilteringOption(LLTexUnit::TFO_ANISOTROPIC);
	}
	else
	{
		mHasMipMaps = false;
	}
	
	llverify(gGL.getTexUnit(0)->bind(this));
James Cook's avatar
James Cook committed
	
James Cook's avatar
James Cook committed
	if (mUseMipMaps)
	{
		if (data_hasmips)
		{
			// NOTE: data_in points to largest image; smaller images
			// are stored BEFORE the largest image
			for (S32 d=mCurrentDiscardLevel; d<=mMaxDiscardLevel; d++)
			{
James Cook's avatar
James Cook committed
				S32 w = getWidth(d);
				S32 h = getHeight(d);
				S32 gl_level = d-mCurrentDiscardLevel;

				mMipLevels = llmax(mMipLevels, gl_level);

James Cook's avatar
James Cook committed
				if (d > mCurrentDiscardLevel)
				{
					data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
				}
				if (is_compressed)
				{
 					S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
					glCompressedTexImage2D(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
James Cook's avatar
James Cook committed
					stop_glerror();
				}
				else
				{
// 					LL_RECORD_BLOCK_TIME(FTM_TEMP4);
James Cook's avatar
James Cook committed

					if(mFormatSwapBytes)
					{
						glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
						stop_glerror();
					}
						
					LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in, mAllowCompression);
					if (gl_level == 0)
					{
						analyzeAlpha(data_in, w, h);
					}
					updatePickMask(w, h, data_in);

James Cook's avatar
James Cook committed
					if(mFormatSwapBytes)
					{
						glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
						stop_glerror();
					}
						
					stop_glerror();
				}
				stop_glerror();
			}			
		}
		else if (!is_compressed)
		{
James Cook's avatar
James Cook committed
			{
				stop_glerror();
				{
// 					LL_RECORD_BLOCK_TIME(FTM_TEMP4);
James Cook's avatar
James Cook committed

					if(mFormatSwapBytes)
					{
						glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
						stop_glerror();
					}

					S32 w = getWidth(mCurrentDiscardLevel);
					S32 h = getHeight(mCurrentDiscardLevel);

					mMipLevels = wpo2(llmax(w, h));

					//use legacy mipmap generation mode (note: making this condional can cause rendering issues)
					// -- but making it not conditional triggers deprecation warnings when core profile is enabled
					//		(some rendering issues while core profile is enabled are acceptable at this point in time)
					if (gGLManager.mGLVersion < 3.f)
					{
						glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE);
					}
					LLImageGL::setManualImage(mTarget, 0, mFormatInternal,
								 w, h, 
James Cook's avatar
James Cook committed
								 mFormatPrimary, mFormatType,
James Cook's avatar
James Cook committed
					stop_glerror();

					updatePickMask(w, h, data_in);

James Cook's avatar
James Cook committed
					if(mFormatSwapBytes)
					{
						glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
						stop_glerror();
					}
					if (gGLManager.mGLVersion >= 3.f)
					{
						glGenerateMipmap(mTarget);
					}	
					stop_glerror();
James Cook's avatar
James Cook committed
				}
			}
			else
			{
				// Create mips by hand
				// ~4x faster than gluBuild2DMipmaps
				S32 width = getWidth(mCurrentDiscardLevel);
				S32 height = getHeight(mCurrentDiscardLevel);
				S32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1;
				S32 w = width, h = height;
James Cook's avatar
James Cook committed
				const U8* prev_mip_data = 0;
				const U8* cur_mip_data = 0;
#ifdef SHOW_ASSERT
				S32 cur_mip_size = 0;
#endif
				mMipLevels = nummips;

James Cook's avatar
James Cook committed
				for (int m=0; m<nummips; m++)
				{
					if (m==0)
					{
						cur_mip_data = data_in;
#ifdef SHOW_ASSERT
						cur_mip_size = width * height * mComponents; 
#endif
James Cook's avatar
James Cook committed
					}
					else
					{
						S32 bytes = w * h * mComponents;
#ifdef SHOW_ASSERT
						llassert(prev_mip_data);
						llassert(cur_mip_size == bytes*4);
#endif
						U8* new_data = new(std::nothrow) U8[bytes];
						if (!new_data)
						{
							stop_glerror();

							if (prev_mip_data)
								delete[] prev_mip_data;
							if (cur_mip_data)
								delete[] cur_mip_data;
							
							mGLTextureCreated = false;
							return FALSE;
						}
						else
						{
#ifdef SHOW_ASSERT
							llassert(prev_mip_data);
							llassert(cur_mip_size == bytes * 4);
#endif
							LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
							cur_mip_data = new_data;
James Cook's avatar
James Cook committed
					}
					llassert(w > 0 && h > 0 && cur_mip_data);
James Cook's avatar
James Cook committed
					{
// 						LL_RECORD_BLOCK_TIME(FTM_TEMP4);
James Cook's avatar
James Cook committed
						if(mFormatSwapBytes)
						{
							glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
							stop_glerror();
						}

						LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression);
James Cook's avatar
James Cook committed
						stop_glerror();
						if (m == 0)
						{
							updatePickMask(w, h, cur_mip_data);
						}
James Cook's avatar
James Cook committed

						if(mFormatSwapBytes)
						{
							glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
							stop_glerror();
						}
					}
					if (prev_mip_data && prev_mip_data != data_in)
					{
						delete[] prev_mip_data;
					}
					prev_mip_data = cur_mip_data;
					w >>= 1;
					h >>= 1;
				}
				if (prev_mip_data && prev_mip_data != data_in)
				{
					delete[] prev_mip_data;
					prev_mip_data = NULL;
			LL_ERRS() << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << LL_ENDL;
James Cook's avatar
James Cook committed
		S32 w = getWidth();
		S32 h = getHeight();
		if (is_compressed)
		{
			S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
			glCompressedTexImage2D(mTarget, 0, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
James Cook's avatar
James Cook committed
			stop_glerror();
		}
		else
		{
			if(mFormatSwapBytes)
			{
				glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
				stop_glerror();
			}

			LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h,
						 mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression);
			updatePickMask(w, h, data_in);

James Cook's avatar
James Cook committed
			stop_glerror();

			if(mFormatSwapBytes)
			{
				glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
				stop_glerror();
			}

		}
	}
	stop_glerror();
BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image)
{
	//not compatible with core GL profile
	llassert(!LLRender::sGLCoreProfile);

	if (gGLManager.mIsDisabled)
	{
		LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL;
		return FALSE;
	}
	llassert(gGLManager.mInited);
	stop_glerror();

	if (discard_level < 0)
	{
		llassert(mCurrentDiscardLevel >= 0);
		discard_level = mCurrentDiscardLevel;
	}
	// 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
	if (!setSize(w, h, raw_image->getComponents(), discard_level))
	{
		LL_WARNS() << "Trying to create a texture with incorrect dimensions!" << LL_ENDL;
		return FALSE;
	}
    if (!mHasExplicitFormat)
    {
        switch (mComponents)
        {
            case 1:
                // Use luminance alpha (for fonts)
                mFormatInternal = GL_LUMINANCE8;
                mFormatPrimary  = GL_LUMINANCE;