diff options
Diffstat (limited to 'indra/llrender/llimagegl.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/llrender/llimagegl.cpp | 836 |
1 files changed, 508 insertions, 328 deletions
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 36ac3ff119..ebed454271 100644..100755 --- 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. * - * 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 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -38,29 +32,35 @@ #include "llimagegl.h" #include "llerror.h" +#include "llfasttimer.h" #include "llimage.h" #include "llmath.h" #include "llgl.h" +#include "llglslshader.h" #include "llrender.h" + //---------------------------------------------------------------------------- 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); + //statics -LLGLuint LLImageGL::sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS] = { 0 }; U32 LLImageGL::sUniqueCount = 0; U32 LLImageGL::sBindCount = 0; -S32 LLImageGL::sGlobalTextureMemoryInBytes = 0; -S32 LLImageGL::sBoundTextureMemoryInBytes = 0; -S32 LLImageGL::sCurBoundTextureMemory = 0; +S32Bytes LLImageGL::sGlobalTextureMemory(0); +S32Bytes LLImageGL::sBoundTextureMemory(0); +S32Bytes 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 ; +bool LLImageGL::sCompressTextures = false; std::set<LLImageGL*> LLImageGL::sImageList; @@ -69,19 +69,13 @@ std::set<LLImageGL*> LLImageGL::sImageList; //**************************************************************************************************** //----------------------- //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 ; +S32 LLImageGL::sMaxCategories = 1 ; + +//optimization for when we don't need to calculate mIsMask +BOOL LLImageGL::sSkipAnalyzeAlpha; -std::vector<S32> LLImageGL::sTextureMemByCategory; -std::vector<S32> LLImageGL::sTextureMemByCategoryBound ; -std::vector<S32> LLImageGL::sTextureCurMemByCategoryBound ; //------------------------ //**************************************************************************************************** //End for texture auditing use only @@ -105,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) { + LL_INFOS() << "Bound: " << texname << " Should bind: " << mTexName << " Default: " << LLImageGL::sDefaultGLTexture->getTexName() << LL_ENDL; + error = TRUE; if (gDebugSession) { @@ -121,7 +124,7 @@ void LLImageGL::checkTexSize() const } else { - llerrs << "Invalid texture bound!" << llendl; + LL_ERRS() << "Invalid texture bound!" << LL_ENDL; } } stop_glerror() ; @@ -129,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 ; @@ -138,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 ; + LL_ERRS() << "wrong texture size and discard level: width: " << + mWidth << " Height: " << mHeight << " Current Level: " << (S32)mCurrentDiscardLevel << LL_ENDL ; } } @@ -164,51 +171,14 @@ BOOL is_little_endian() return (*c == 0x78) ; } //static -void LLImageGL::initClass(S32 num_catagories) +void LLImageGL::initClass(S32 num_catagories, BOOL skip_analyze_alpha /* = false */) { - sMaxCatagories = num_catagories ; - - sTextureMemByCategory.resize(sMaxCatagories); - sTextureMemByCategoryBound.resize(sMaxCatagories) ; - sTextureCurMemByCategoryBound.resize(sMaxCatagories) ; + sSkipAnalyzeAlpha = skip_analyze_alpha; } //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 @@ -228,7 +198,7 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) case GL_RGBA: return 32; case GL_BGRA: return 32; // Used for QuickTime media textures on the Mac default: - llerrs << "LLImageGL::Unknown format: " << dataformat << llendl; + LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL; return 0; } } @@ -263,46 +233,28 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat) case GL_RGBA: return 4; case GL_BGRA: return 4; // Used for QuickTime media textures on the Mac default: - llerrs << "LLImageGL::Unknown format: " << dataformat << llendl; + LL_ERRS() << "LLImageGL::Unknown format: " << dataformat << LL_ENDL; return 0; } } //---------------------------------------------------------------------------- +static LLTrace::BlockTimerStatHandle FTM_IMAGE_UPDATE_STATS("Image Stats"); // static void LLImageGL::updateStats(F32 current_time) { + LL_RECORD_BLOCK_TIME(FTM_IMAGE_UPDATE_STATS); 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 ; - } - } + sBoundTextureMemory = sCurBoundTextureMemory; + sCurBoundTextureMemory = S32Bytes(0); } //static -S32 LLImageGL::updateBoundTexMem(const S32 mem, const S32 ncomponents, S32 category) +S32 LLImageGL::updateBoundTexMem(const S32Bytes mem, const S32 ncomponents, S32 category) { - if(gAuditTexture && ncomponents > 0 && category > -1) - { - sTextureCurBoundCounter[getTextureCounterIndex(mem / ncomponents)]++ ; - sTextureCurMemByCategoryBound[category] += mem ; - } - LLImageGL::sCurBoundTextureMemory += mem ; - return LLImageGL::sCurBoundTextureMemory; + return LLImageGL::sCurBoundTextureMemory.value(); } //---------------------------------------------------------------------------- @@ -325,8 +277,10 @@ void LLImageGL::destroyGL(BOOL save_state) if (save_state && glimage->isGLTextureCreated() && glimage->mComponents) { glimage->mSaveData = new LLImageRaw; + glimage->claimMem(glimage->mSaveData); if(!glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData, false)) //necessary, keep it. { + glimage->disclaimMem(glimage->mSaveData); glimage->mSaveData = NULL ; } } @@ -347,7 +301,7 @@ void LLImageGL::restoreGL() LLImageGL* glimage = *iter; if(glimage->getTexName()) { - llerrs << "tex name is not 0." << llendl ; + LL_ERRS() << "tex name is not 0." << LL_ENDL ; } if (glimage->mSaveData.notNull()) { @@ -361,6 +315,18 @@ void LLImageGL::restoreGL() } } +//static +void LLImageGL::dirtyTexOptions() +{ + for (std::set<LLImageGL*>::iterator iter = sImageList.begin(); + iter != sImageList.end(); iter++) + { + LLImageGL* glimage = *iter; + glimage->mTexOptionsDirty = true; + stop_glerror(); + } + +} //---------------------------------------------------------------------------- //for server side use only. @@ -388,7 +354,8 @@ BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, B //---------------------------------------------------------------------------- LLImageGL::LLImageGL(BOOL usemipmaps) - : mSaveData(0) +: LLTrace::MemTrackable<LLImageGL>("LLImageGL"), + mSaveData(0) { init(usemipmaps); setSize(0, 0, 0); @@ -397,7 +364,8 @@ LLImageGL::LLImageGL(BOOL usemipmaps) } LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps) - : mSaveData(0) +: LLTrace::MemTrackable<LLImageGL>("LLImageGL"), + mSaveData(0) { llassert( components <= 4 ); init(usemipmaps); @@ -407,7 +375,8 @@ LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps) } LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps) - : mSaveData(0) +: LLTrace::MemTrackable<LLImageGL>("LLImageGL"), + mSaveData(0) { init(usemipmaps); setSize(0, 0, 0); @@ -421,8 +390,7 @@ LLImageGL::~LLImageGL() { LLImageGL::cleanup(); sImageList.erase(this); - delete [] mPickMask; - mPickMask = NULL; + freePickMask(); sCount--; } @@ -432,7 +400,7 @@ void LLImageGL::init(BOOL usemipmaps) // so that it is obvious by visual inspection if we forgot to // init a field. - mTextureMemory = 0; + mTextureMemory = (S32Bytes)0; mLastBindTime = 0.f; mPickMask = NULL; @@ -456,10 +424,13 @@ void LLImageGL::init(BOOL usemipmaps) mDiscardLevelInAtlas = -1 ; mTexelsInAtlas = 0 ; mTexelsInGLTexture = 0 ; + + mAllowCompression = true; mTarget = GL_TEXTURE_2D; mBindTarget = LLTexUnit::TT_TEXTURE; mHasMipMaps = false; + mMipLevels = -1; mIsResident = 0; @@ -488,6 +459,8 @@ void LLImageGL::cleanup() { destroyGLTexture(); } + freePickMask(); + mSaveData = NULL; // deletes data } @@ -514,26 +487,24 @@ bool LLImageGL::checkSize(S32 width, S32 height) return check_power_of_two(width) && check_power_of_two(height); } -void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) +void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents, S32 discard_level) { if (width != mWidth || height != mHeight || ncomponents != mComponents) { // Check if dimensions are a power of two! if (!checkSize(width,height)) { - llerrs << llformat("Texture has non power of two dimension: %dx%d",width,height) << llendl; + LL_ERRS() << llformat("Texture has non power of two dimension: %dx%d",width,height) << LL_ENDL; } if (mTexName) { -// llwarns << "Setting Size of LLImageGL with existing mTexName = " << mTexName << llendl; +// LL_WARNS() << "Setting Size of LLImageGL with existing mTexName = " << mTexName << LL_ENDL; destroyGLTexture(); } // pickmask validity depends on old image size, delete it - delete [] mPickMask; - mPickMask = NULL; - mPickMaskWidth = mPickMaskHeight = 0; + freePickMask(); mWidth = width; mHeight = height; @@ -547,6 +518,11 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) width >>= 1; height >>= 1; } + + if(discard_level > 0) + { + mMaxDiscardLevel = llmax(mMaxDiscardLevel, (S8)discard_level); + } } else { @@ -560,7 +536,7 @@ void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents) // virtual void LLImageGL::dump() { - llinfos << "mMaxDiscardLevel " << S32(mMaxDiscardLevel) + LL_INFOS() << "mMaxDiscardLevel " << S32(mMaxDiscardLevel) << " mLastBindTime " << mLastBindTime << " mTarget " << S32(mTarget) << " mBindTarget " << S32(mBindTarget) @@ -575,12 +551,12 @@ void LLImageGL::dump() #if DEBUG_MISS << " mMissed " << mMissed #endif - << llendl; + << LL_ENDL; - llinfos << " mTextureMemory " << mTextureMemory + LL_INFOS() << " mTextureMemory " << mTextureMemory << " mTexNames " << mTexName << " mIsResident " << S32(mIsResident) - << llendl; + << LL_ENDL; } //---------------------------------------------------------------------------- @@ -589,7 +565,7 @@ void LLImageGL::forceUpdateBindStats(void) const mLastBindTime = sLastFrameTime; } -BOOL LLImageGL::updateBindStats(S32 tex_mem) const +BOOL LLImageGL::updateBindStats(S32Bytes tex_mem) const { if (mTexName != 0) { @@ -642,16 +618,34 @@ void LLImageGL::setImage(const LLImageRaw* imageraw) setImage(rawdata, FALSE); } +static LLTrace::BlockTimerStatHandle FTM_SET_IMAGE("setImage"); void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { + LL_RECORD_BLOCK_TIME(FTM_SET_IMAGE); bool is_compressed = false; if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { is_compressed = true; } - + + + + 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)); + if (mUseMipMaps) { if (data_hasmips) @@ -664,6 +658,9 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) S32 w = getWidth(d); S32 h = getHeight(d); S32 gl_level = d-mCurrentDiscardLevel; + + mMipLevels = llmax(mMipLevels, gl_level); + if (d > mCurrentDiscardLevel) { data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment @@ -676,7 +673,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } else { -// LLFastTimer t2(FTM_TEMP4); +// LL_RECORD_BLOCK_TIME(FTM_TEMP4); if(mFormatSwapBytes) { @@ -684,7 +681,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) stop_glerror(); } - LLImageGL::setManualImage(mTarget, gl_level, mFormatInternal, w, h, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in); + 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); @@ -706,10 +703,9 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { if (mAutoGenMips) { - glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE); stop_glerror(); { -// LLFastTimer t2(FTM_TEMP4); +// LL_RECORD_BLOCK_TIME(FTM_TEMP4); if(mFormatSwapBytes) { @@ -720,10 +716,20 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) 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 (!LLRender::sGLCoreProfile) + { + glTexParameteri(mTarget, GL_GENERATE_MIPMAP, GL_TRUE); + } + LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h, mFormatPrimary, mFormatType, - data_in); + data_in, mAllowCompression); analyzeAlpha(data_in, w, h); stop_glerror(); @@ -734,49 +740,76 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); stop_glerror(); } + + if (LLRender::sGLCoreProfile) + { + glGenerateMipmap(mTarget); + } + stop_glerror(); } } else { // Create mips by hand - // about 30% faster than autogen on ATI 9800, 50% slower on nVidia 4800 // ~4x faster than gluBuild2DMipmaps S32 width = getWidth(mCurrentDiscardLevel); S32 height = getHeight(mCurrentDiscardLevel); S32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1; S32 w = width, h = height; + + + const U8* new_data = 0; + (void)new_data; + const U8* prev_mip_data = 0; const U8* cur_mip_data = 0; - S32 prev_mip_size = 0; +#ifdef SHOW_ASSERT S32 cur_mip_size = 0; +#endif + mMipLevels = nummips; + 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 } else { S32 bytes = w * h * mComponents; +#ifdef SHOW_ASSERT llassert(prev_mip_data); - llassert(prev_mip_size == bytes*4); + llassert(cur_mip_size == bytes*4); +#endif U8* new_data = new U8[bytes]; + +#ifdef SHOW_ASSERT + llassert(prev_mip_data); + llassert(cur_mip_size == bytes*4); llassert_always(new_data); +#endif + LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents); cur_mip_data = new_data; +#ifdef SHOW_ASSERT cur_mip_size = bytes; +#endif + } llassert(w > 0 && h > 0 && cur_mip_data); + (void)cur_mip_data; { -// LLFastTimer t1(FTM_TEMP4); +// LL_RECORD_BLOCK_TIME(FTM_TEMP4); if(mFormatSwapBytes) { glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); stop_glerror(); } - LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data); + LLImageGL::setManualImage(mTarget, m, mFormatInternal, w, h, mFormatPrimary, mFormatType, cur_mip_data, mAllowCompression); if (m == 0) { analyzeAlpha(data_in, w, h); @@ -798,7 +831,6 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) delete[] prev_mip_data; } prev_mip_data = cur_mip_data; - prev_mip_size = cur_mip_size; w >>= 1; h >>= 1; } @@ -811,12 +843,12 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } else { - llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << llendl; + LL_ERRS() << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << LL_ENDL; } - mHasMipMaps = true; } else { + mMipLevels = 0; S32 w = getWidth(); S32 h = getHeight(); if (is_compressed) @@ -834,7 +866,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } LLImageGL::setManualImage(mTarget, 0, mFormatInternal, w, h, - mFormatPrimary, mFormatType, (GLvoid *)data_in); + mFormatPrimary, mFormatType, (GLvoid *)data_in, mAllowCompression); analyzeAlpha(data_in, w, h); updatePickMask(w, h, data_in); @@ -848,7 +880,6 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } } - mHasMipMaps = false; } stop_glerror(); mGLTextureCreated = true; @@ -856,9 +887,12 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) { + //not compatible with core GL profile + llassert(!LLRender::sGLCoreProfile); + if (gGLManager.mIsDisabled) { - llwarns << "Trying to create a texture while GL is disabled!" << llendl; + LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; return FALSE; } llassert(gGLManager.mInited); @@ -869,43 +903,42 @@ BOOL LLImageGL::preAddToAtlas(S32 discard_level, const LLImageRaw* raw_image) 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()); + setSize(w, h, raw_image->getComponents(), discard_level); if( !mHasExplicitFormat ) { switch (mComponents) { - case 1: + case 1: // Use luminance alpha (for fonts) mFormatInternal = GL_LUMINANCE8; mFormatPrimary = GL_LUMINANCE; mFormatType = GL_UNSIGNED_BYTE; break; - case 2: + case 2: // Use luminance alpha (for fonts) mFormatInternal = GL_LUMINANCE8_ALPHA8; mFormatPrimary = GL_LUMINANCE_ALPHA; mFormatType = GL_UNSIGNED_BYTE; break; - case 3: + case 3: mFormatInternal = GL_RGB8; mFormatPrimary = GL_RGB; mFormatType = GL_UNSIGNED_BYTE; break; - case 4: + case 4: mFormatInternal = GL_RGBA8; mFormatPrimary = GL_RGBA; mFormatType = GL_UNSIGNED_BYTE; break; - default: - llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl; + default: + LL_ERRS() << "Bad number of components for texture: " << (U32)getComponents() << LL_ENDL; } } @@ -948,12 +981,14 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 } if (mTexName == 0) { - llwarns << "Setting subimage on image without GL texture" << llendl; + // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18 + //LL_WARNS() << "Setting subimage on image without GL texture" << LL_ENDL; return FALSE; } if (datap == NULL) { - llwarns << "Setting subimage on image with NULL datap" << llendl; + // *TODO: Re-enable warning? Ran into thread locking issues? DK 2011-02-18 + //LL_WARNS() << "Setting subimage on image with NULL datap" << LL_ENDL; return FALSE; } @@ -967,7 +1002,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 if (mUseMipMaps) { dump(); - llerrs << "setSubImage called with mipmapped image (not supported)" << llendl; + LL_ERRS() << "setSubImage called with mipmapped image (not supported)" << LL_ENDL; } llassert_always(mCurrentDiscardLevel == 0); llassert_always(x_pos >= 0 && y_pos >= 0); @@ -976,28 +1011,28 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 (y_pos + height) > getHeight()) { dump(); - llerrs << "Subimage not wholly in target image!" + LL_ERRS() << "Subimage not wholly in target image!" << " x_pos " << x_pos << " y_pos " << y_pos << " width " << width << " height " << height << " getWidth() " << getWidth() << " getHeight() " << getHeight() - << llendl; + << LL_ENDL; } if ((x_pos + width) > data_width || (y_pos + height) > data_height) { dump(); - llerrs << "Subimage not wholly in source image!" + LL_ERRS() << "Subimage not wholly in source image!" << " x_pos " << x_pos << " y_pos " << y_pos << " width " << width << " height " << height << " source_width " << data_width << " source_height " << data_height - << llendl; + << LL_ENDL; } @@ -1013,7 +1048,7 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 datap += (y_pos * data_width + x_pos) * getComponents(); // Update the GL texture BOOL res = gGL.getTexUnit(0)->bindManual(mBindTarget, mTexName); - if (!res) llerrs << "LLImageGL::setSubImage(): bindTexture failed" << llendl; + if (!res) LL_ERRS() << "LLImageGL::setSubImage(): bindTexture failed" << LL_ENDL; stop_glerror(); glTexSubImage2D(mTarget, 0, x_pos, y_pos, @@ -1044,7 +1079,6 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_ { if (gGL.getTexUnit(0)->bind(this, false, true)) { - //checkTexSize() ; glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height); mGLTextureCreated = true; stop_glerror(); @@ -1057,34 +1091,137 @@ BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_ } // static +static LLTrace::BlockTimerStatHandle FTM_GENERATE_TEXTURES("generate textures"); void LLImageGL::generateTextures(S32 numTextures, U32 *textures) { - glGenTextures(numTextures, (GLuint*)textures); + LL_RECORD_BLOCK_TIME(FTM_GENERATE_TEXTURES); + glGenTextures(numTextures, textures); } // static void LLImageGL::deleteTextures(S32 numTextures, U32 *textures) { - for (S32 i = 0; i < numTextures; i++) + if (gGLManager.mInited) { - sDeadTextureList.push_back(textures[i]); + glDeleteTextures(numTextures, textures); } } // static -void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels) +static LLTrace::BlockTimerStatHandle FTM_SET_MANUAL_IMAGE("setManualImage"); +void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void *pixels, bool allow_compression) { - glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, pixels); + LL_RECORD_BLOCK_TIME(FTM_SET_MANUAL_IMAGE); + bool use_scratch = false; + U32* scratch = NULL; + if (LLRender::sGLCoreProfile) + { + if (pixformat == GL_ALPHA && pixtype == GL_UNSIGNED_BYTE) + { //GL_ALPHA is deprecated, convert to RGBA + use_scratch = true; + scratch = new U32[width*height]; + + U32 pixel_count = (U32) (width*height); + for (U32 i = 0; i < pixel_count; i++) + { + U8* pix = (U8*) &scratch[i]; + pix[0] = pix[1] = pix[2] = 0; + pix[3] = ((U8*) pixels)[i]; + } + + pixformat = GL_RGBA; + intformat = GL_RGBA8; + } + + if (pixformat == GL_LUMINANCE_ALPHA && pixtype == GL_UNSIGNED_BYTE) + { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA + use_scratch = true; + scratch = new U32[width*height]; + + U32 pixel_count = (U32) (width*height); + for (U32 i = 0; i < pixel_count; i++) + { + U8 lum = ((U8*) pixels)[i*2+0]; + U8 alpha = ((U8*) pixels)[i*2+1]; + + U8* pix = (U8*) &scratch[i]; + pix[0] = pix[1] = pix[2] = lum; + pix[3] = alpha; + } + + pixformat = GL_RGBA; + intformat = GL_RGBA8; + } + + if (pixformat == GL_LUMINANCE && pixtype == GL_UNSIGNED_BYTE) + { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB + use_scratch = true; + scratch = new U32[width*height]; + + U32 pixel_count = (U32) (width*height); + for (U32 i = 0; i < pixel_count; i++) + { + U8 lum = ((U8*) pixels)[i]; + + U8* pix = (U8*) &scratch[i]; + pix[0] = pix[1] = pix[2] = lum; + pix[3] = 255; + } + + pixformat = GL_RGBA; + intformat = GL_RGB8; + } + } + + if (LLImageGL::sCompressTextures && allow_compression) + { + switch (intformat) + { + case GL_RGB: + case GL_RGB8: + intformat = GL_COMPRESSED_RGB; + break; + case GL_RGBA: + case GL_RGBA8: + intformat = GL_COMPRESSED_RGBA; + break; + case GL_LUMINANCE: + case GL_LUMINANCE8: + intformat = GL_COMPRESSED_LUMINANCE; + break; + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE8_ALPHA8: + intformat = GL_COMPRESSED_LUMINANCE_ALPHA; + break; + case GL_ALPHA: + case GL_ALPHA8: + intformat = GL_COMPRESSED_ALPHA; + break; + default: + LL_WARNS() << "Could not compress format: " << std::hex << intformat << LL_ENDL; + break; + } + } + + stop_glerror(); + glTexImage2D(target, miplevel, intformat, width, height, 0, pixformat, pixtype, use_scratch ? scratch : pixels); stop_glerror(); + + if (use_scratch) + { + delete [] scratch; + } } //create an empty GL texture: just create a texture name //the texture is assiciate with some image by calling glTexImage outside LLImageGL +static LLTrace::BlockTimerStatHandle FTM_CREATE_GL_TEXTURE1("createGLTexture()"); BOOL LLImageGL::createGLTexture() { + LL_RECORD_BLOCK_TIME(FTM_CREATE_GL_TEXTURE1); if (gGLManager.mIsDisabled) { - llwarns << "Trying to create a texture while GL is disabled!" << llendl; + LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; return FALSE; } @@ -1095,24 +1232,27 @@ BOOL LLImageGL::createGLTexture() if(mTexName) { - glDeleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ; + LLImageGL::deleteTextures(1, (reinterpret_cast<GLuint*>(&mTexName))) ; } - glGenTextures(1, (GLuint*)&mTexName); + + LLImageGL::generateTextures(1, &mTexName); stop_glerror(); if (!mTexName) { - llerrs << "LLImageGL::createGLTexture failed to make an empty texture" << llendl; + LL_ERRS() << "LLImageGL::createGLTexture failed to make an empty texture" << LL_ENDL; } return TRUE ; } +static LLTrace::BlockTimerStatHandle FTM_CREATE_GL_TEXTURE2("createGLTexture(raw)"); BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/, BOOL to_create, S32 category) { + LL_RECORD_BLOCK_TIME(FTM_CREATE_GL_TEXTURE2); if (gGLManager.mIsDisabled) { - llwarns << "Trying to create a texture while GL is disabled!" << llendl; + LL_WARNS() << "Trying to create a texture while GL is disabled!" << LL_ENDL; return FALSE; } @@ -1125,8 +1265,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S 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 raw_w = imageraw->getWidth() ; S32 raw_h = imageraw->getHeight() ; @@ -1134,36 +1273,36 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S S32 h = raw_h << discard_level; // setSize may call destroyGLTexture if the size does not match - setSize(w, h, imageraw->getComponents()); + setSize(w, h, imageraw->getComponents(), discard_level); if( !mHasExplicitFormat ) { switch (mComponents) { - case 1: + case 1: // Use luminance alpha (for fonts) mFormatInternal = GL_LUMINANCE8; mFormatPrimary = GL_LUMINANCE; mFormatType = GL_UNSIGNED_BYTE; break; - case 2: + case 2: // Use luminance alpha (for fonts) mFormatInternal = GL_LUMINANCE8_ALPHA8; mFormatPrimary = GL_LUMINANCE_ALPHA; mFormatType = GL_UNSIGNED_BYTE; break; - case 3: + case 3: mFormatInternal = GL_RGB8; mFormatPrimary = GL_RGB; mFormatType = GL_UNSIGNED_BYTE; break; - case 4: + case 4: mFormatInternal = GL_RGBA8; mFormatPrimary = GL_RGBA; mFormatType = GL_UNSIGNED_BYTE; break; - default: - llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl; + default: + LL_ERRS() << "Bad number of components for texture: " << (U32)getComponents() << LL_ENDL; } calcAlphaChannelOffsetAndStride() ; @@ -1177,14 +1316,17 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S return TRUE ; } - setCategory(category) ; + setCategory(category); const U8* rawdata = imageraw->getData(); return createGLTexture(discard_level, rawdata, FALSE, usename); } +static LLTrace::BlockTimerStatHandle FTM_CREATE_GL_TEXTURE3("createGLTexture3(data)"); BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename) { + LL_RECORD_BLOCK_TIME(FTM_CREATE_GL_TEXTURE3); llassert(data_in); + stop_glerror(); if (discard_level < 0) { @@ -1213,13 +1355,16 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ stop_glerror(); { llverify(gGL.getTexUnit(0)->bind(this)); + stop_glerror(); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0); + stop_glerror(); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level); + stop_glerror(); } } if (!mTexName) { - llerrs << "LLImageGL::createGLTexture failed to make texture" << llendl; + LL_ERRS() << "LLImageGL::createGLTexture failed to make texture" << LL_ENDL; } if (mUseMipMaps) @@ -1249,26 +1394,19 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ if (old_name != 0) { - sGlobalTextureMemoryInBytes -= mTextureMemory; - - if(gAuditTexture) - { - decTextureCounter(mTextureMemory, mComponents, mCategory) ; - } + sGlobalTextureMemory -= mTextureMemory; LLImageGL::deleteTextures(1, &old_name); stop_glerror(); } - mTextureMemory = getMipBytes(discard_level); - sGlobalTextureMemoryInBytes += mTextureMemory; + disclaimMem(mTextureMemory); + mTextureMemory = (S32Bytes)getMipBytes(discard_level); + claimMem(mTextureMemory); + sGlobalTextureMemory += mTextureMemory; 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; return TRUE; @@ -1277,7 +1415,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compressed_ok) const { llassert_always(sAllowReadBackRaw) ; - //llerrs << "should not call this function!" << llendl ; + //LL_ERRS() << "should not call this function!" << LL_ENDL ; if (discard_level < 0) { @@ -1315,15 +1453,15 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre } if(width < glwidth) { - llwarns << "texture size is smaller than it should be." << llendl ; - llwarns << "width: " << width << " glwidth: " << glwidth << " mWidth: " << mWidth << - " mCurrentDiscardLevel: " << (S32)mCurrentDiscardLevel << " discard_level: " << (S32)discard_level << llendl ; + LL_WARNS() << "texture size is smaller than it should be." << LL_ENDL ; + LL_WARNS() << "width: " << width << " glwidth: " << glwidth << " mWidth: " << mWidth << + " mCurrentDiscardLevel: " << (S32)mCurrentDiscardLevel << " discard_level: " << (S32)discard_level << LL_ENDL ; return FALSE ; } if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4) { - llerrs << llformat("LLImageGL::readBackRaw: bogus params: %d x %d x %d",width,height,ncomponents) << llendl; + LL_ERRS() << llformat("LLImageGL::readBackRaw: bogus params: %d x %d x %d",width,height,ncomponents) << LL_ENDL; } LLGLint is_compressed = 0; @@ -1336,7 +1474,7 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre GLenum error ; while((error = glGetError()) != GL_NO_ERROR) { - llwarns << "GL Error happens before reading back texture. Error code: " << error << llendl ; + LL_WARNS() << "GL Error happens before reading back texture. Error code: " << error << LL_ENDL ; } //----------------------------------------------------------------------------------------------- @@ -1346,8 +1484,8 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes); if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes)) { - llwarns << "Memory allocation failed for reading back texture. Size is: " << glbytes << llendl ; - llwarns << "width: " << width << "height: " << height << "components: " << ncomponents << llendl ; + LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ; + LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ; return FALSE ; } @@ -1358,8 +1496,8 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre { if(!imageraw->allocateDataSize(width, height, ncomponents)) { - llwarns << "Memory allocation failed for reading back texture." << llendl ; - llwarns << "width: " << width << "height: " << height << "components: " << ncomponents << llendl ; + LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ; + LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ; return FALSE ; } @@ -1370,12 +1508,12 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre //----------------------------------------------------------------------------------------------- if((error = glGetError()) != GL_NO_ERROR) { - llwarns << "GL Error happens after reading back texture. Error code: " << error << llendl ; + LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ; imageraw->deleteData() ; while((error = glGetError()) != GL_NO_ERROR) { - llwarns << "GL Error happens after reading back texture. Error code: " << error << llendl ; + LL_WARNS() << "GL Error happens after reading back texture. Error code: " << error << LL_ENDL ; } return FALSE ; @@ -1387,21 +1525,11 @@ BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre void LLImageGL::deleteDeadTextures() { - while (!sDeadTextureList.empty()) + bool reset = false; + + if (reset) { - GLuint tex = sDeadTextureList.front(); - sDeadTextureList.pop_front(); - for (int i = 0; i < gGLManager.mNumTextureUnits; i++) - { - if (sCurrentBoundTextures[i] == tex) - { - gGL.getTexUnit(i)->unbind(LLTexUnit::TT_TEXTURE); - stop_glerror(); - } - } - - glDeleteTextures(1, &tex); - stop_glerror(); + gGL.getTexUnit(0)->activate(); } } @@ -1409,24 +1537,32 @@ void LLImageGL::destroyGLTexture() { if (mTexName != 0) { - if(mTextureMemory) + if(mTextureMemory != S32Bytes(0)) { - if(gAuditTexture) - { - decTextureCounter(mTextureMemory, mComponents, mCategory) ; - } - sGlobalTextureMemoryInBytes -= mTextureMemory; - mTextureMemory = 0; + sGlobalTextureMemory -= mTextureMemory; + disclaimMem(mTextureMemory); + mTextureMemory = (S32Bytes)0; } LLImageGL::deleteTextures(1, &mTexName); - mTexName = 0; mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel. + mTexName = 0; mGLTextureCreated = FALSE ; - } + } } - +//force to invalidate the gl texture, most likely a sculpty texture +void LLImageGL::forceToInvalidateGLTexture() +{ + if (mTexName != 0) + { + destroyGLTexture(); + } + else + { + mCurrentDiscardLevel = -1 ; //invalidate mCurrentDiscardLevel. + } +} //---------------------------------------------------------------------------- @@ -1544,6 +1680,12 @@ BOOL LLImageGL::getBoundRecently() const return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME); } +BOOL LLImageGL::getIsAlphaMask() const +{ + llassert_always(!sSkipAnalyzeAlpha); + return mIsMask; +} + void LLImageGL::setTarget(const LLGLenum target, const LLTexUnit::eTextureType bind_target) { mTarget = target; @@ -1632,41 +1774,107 @@ void LLImageGL::calcAlphaChannelOffsetAndStride() 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; + LL_WARNS() << "Cannot analyze alpha for image with format type " << std::hex << mFormatType << std::dec << LL_ENDL; mNeedsAlphaAndPickMask = FALSE ; mIsMask = FALSE; } } -void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h) +void LLImageGL::analyzeAlpha(const void* data_in, U32 w, U32 h) { - if(!mNeedsAlphaAndPickMask) + if(sSkipAnalyzeAlpha || !mNeedsAlphaAndPickMask) { return ; } U32 length = w * h; - const GLubyte* current = ((const GLubyte*) data_in) + mAlphaOffset ; + U32 alphatotal = 0; - S32 sample[16]; - memset(sample, 0, sizeof(S32)*16); - - for (U32 i = 0; i < length; i++) + 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 { - ++sample[*current/16]; - current += mAlphaStride ; + 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 - U32 total = 0; - for (U32 i = 4; i < 11; i++) + // 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. + + U32 midrangetotal = 0; + for (U32 i = 2; i < 13; i++) + { + midrangetotal += sample[i]; + } + U32 lowerhalftotal = 0; + for (U32 i = 0; i < 8; i++) + { + lowerhalftotal += sample[i]; + } + U32 upperhalftotal = 0; + for (U32 i = 8; i < 16; i++) { - total += sample[i]; + upperhalftotal += sample[i]; } - if (total > length/16) + if (midrangetotal > length/48 || // 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; + mIsMask = FALSE; // not suitable for masking } else { @@ -1675,6 +1883,37 @@ void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h) } //---------------------------------------------------------------------------- +U32 LLImageGL::createPickMask(S32 pWidth, S32 pHeight) +{ + U32 pick_width = pWidth/2 + 1; + U32 pick_height = pHeight/2 + 1; + + U32 size = pick_width * pick_height; + size = (size + 7) / 8; // pixelcount-to-bits + mPickMask = new U8[size]; + claimMem(size); + mPickMaskWidth = pick_width - 1; + mPickMaskHeight = pick_height - 1; + + memset(mPickMask, 0, sizeof(U8) * size); + + return size; +} + +//---------------------------------------------------------------------------- +void LLImageGL::freePickMask() +{ + // pickmask validity depends on old image size, delete it + if (mPickMask != NULL) + { + disclaimMem((mPickMaskWidth * mPickMaskHeight + 7) / 8); + delete [] mPickMask; + } + mPickMask = NULL; + mPickMaskWidth = mPickMaskHeight = 0; +} + +//---------------------------------------------------------------------------- void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) { if(!mNeedsAlphaAndPickMask) @@ -1682,9 +1921,7 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) return ; } - delete [] mPickMask; - mPickMask = NULL; - mPickMaskWidth = mPickMaskHeight = 0; + freePickMask(); if (mFormatType != GL_UNSIGNED_BYTE || mFormatPrimary != GL_RGBA) @@ -1693,16 +1930,11 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) return; } - U32 pick_width = width/2 + 1; - U32 pick_height = height/2 + 1; - - U32 size = pick_width * pick_height; - size = (size + 7) / 8; // pixelcount-to-bits - mPickMask = new U8[size]; - mPickMaskWidth = pick_width; - mPickMaskHeight = pick_height; - - memset(mPickMask, 0, sizeof(U8) * size); +#ifdef SHOW_ASSERT + const U32 pickSize = createPickMask(width, height); +#else // SHOW_ASSERT + createPickMask(width, height); +#endif // SHOW_ASSERT U32 pick_bit = 0; @@ -1716,7 +1948,7 @@ void LLImageGL::updatePickMask(S32 width, S32 height, const U8* data_in) { U32 pick_idx = pick_bit/8; U32 pick_offset = pick_bit%8; - llassert(pick_idx < size); + llassert(pick_idx < pickSize); mPickMask[pick_idx] |= 1 << pick_offset; } @@ -1732,31 +1964,41 @@ BOOL LLImageGL::getMask(const LLVector2 &tc) if (mPickMask) { - F32 u = tc.mV[0] - floorf(tc.mV[0]); - F32 v = tc.mV[1] - floorf(tc.mV[1]); + 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; - llassert(false); + // removing assert per EXT-4388 + // llassert(false); } - llassert(mPickMaskWidth > 0 && mPickMaskHeight > 0); - S32 x = llfloor(u * mPickMaskWidth); S32 y = llfloor(v * mPickMaskHeight); - if (LL_UNLIKELY(x >= mPickMaskWidth)) + if (LL_UNLIKELY(x > mPickMaskWidth)) { LL_WARNS_ONCE("render") << "Ooh, width overrun on pick mask read, that coulda been bad." << LL_ENDL; - x = llmax(0, mPickMaskWidth-1); + x = llmax((U16)0, mPickMaskWidth); } - if (LL_UNLIKELY(y >= mPickMaskHeight)) + if (LL_UNLIKELY(y > mPickMaskHeight)) { LL_WARNS_ONCE("render") << "Ooh, height overrun on pick mask read, that woulda been bad." << LL_ENDL; - y = llmax(0, mPickMaskHeight-1); + y = llmax((U16)0, mPickMaskHeight); } S32 idx = y*mPickMaskWidth+x; @@ -1768,68 +2010,6 @@ 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 ; |