269 lines
7.7 KiB
C++
269 lines
7.7 KiB
C++
/**
|
|
* @file lltextureatlasmanager.cpp
|
|
* @brief LLTextureAtlasManager class implementation.
|
|
*
|
|
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "linden_common.h"
|
|
#include "llerror.h"
|
|
#include "llmath.h"
|
|
#include "lltextureatlas.h"
|
|
#include "lltextureatlasmanager.h"
|
|
#include "llspatialpartition.h"
|
|
|
|
const S8 MAX_NUM_EMPTY_ATLAS = 2 ;
|
|
const F32 MIN_ATLAS_FULLNESS = 0.6f ;
|
|
|
|
//*********************************************************************************************
|
|
//implementation of class LLTextureAtlasInfo
|
|
//*********************************************************************************************
|
|
LLTextureAtlasSlot::LLTextureAtlasSlot(LLTextureAtlas* atlasp, LLSpatialGroup* groupp, S16 col, S16 row, F32 xoffset, F32 yoffset, S8 slot_width) :
|
|
mAtlasp(atlasp),
|
|
mGroupp(groupp),
|
|
mCol(col),
|
|
mRow(row),
|
|
mReservedSlotWidth(slot_width),
|
|
mValid(FALSE),
|
|
mUpdatedTime(0),
|
|
mTexCoordOffset(xoffset, yoffset),
|
|
mTexCoordScale(1.f, 1.f)
|
|
{
|
|
llassert_always(mAtlasp || mGroupp || mReservedSlotWidth) ;
|
|
}
|
|
|
|
LLTextureAtlasSlot::~LLTextureAtlasSlot()
|
|
{
|
|
if(mAtlasp)
|
|
{
|
|
mAtlasp->releaseSlot(mCol, mRow, mReservedSlotWidth) ;
|
|
if(mAtlasp->isEmpty())
|
|
{
|
|
LLTextureAtlasManager::getInstance()->releaseAtlas(mAtlasp) ;
|
|
}
|
|
mAtlasp = NULL ;
|
|
}
|
|
}
|
|
|
|
//void LLTextureAtlasSlot::setAtlas(LLTextureAtlas* atlasp)
|
|
//{
|
|
// mAtlasp = atlasp ;
|
|
//}
|
|
//void LLTextureAtlasSlot::setSlotPos(S16 col, S16 row)
|
|
//{
|
|
// mCol = col ;
|
|
// mRow = row ;
|
|
//}
|
|
//void LLTextureAtlasSlot::setSlotWidth(S8 width)
|
|
//{
|
|
// //slot is a square with each edge length a power-of-two number
|
|
// mReservedSlotWidth = width ;
|
|
//}
|
|
//void LLTextureAtlasSlot::setTexCoordOffset(F32 xoffset, F32 yoffset)
|
|
//{
|
|
// mTexCoordOffset.mV[0] = xoffset ;
|
|
// mTexCoordOffset.mV[1] = yoffset ;
|
|
//}
|
|
|
|
void LLTextureAtlasSlot::setSpatialGroup(LLSpatialGroup* groupp)
|
|
{
|
|
mGroupp = groupp ;
|
|
}
|
|
void LLTextureAtlasSlot::setTexCoordScale(F32 xscale, F32 yscale)
|
|
{
|
|
mTexCoordScale.mV[0] = xscale ;
|
|
mTexCoordScale.mV[1] = yscale ;
|
|
}
|
|
//*********************************************************************************************
|
|
//END of implementation of class LLTextureAtlasInfo
|
|
//*********************************************************************************************
|
|
|
|
//*********************************************************************************************
|
|
//implementation of class LLTextureAtlasManager
|
|
//*********************************************************************************************
|
|
LLTextureAtlasManager::LLTextureAtlasManager() :
|
|
mAtlasMap(4),
|
|
mEmptyAtlasMap(4)
|
|
{
|
|
}
|
|
|
|
LLTextureAtlasManager::~LLTextureAtlasManager()
|
|
{
|
|
for(S32 i = 0 ; i < 4 ; i++)
|
|
{
|
|
for(ll_texture_atlas_list_t::iterator j = mAtlasMap[i].begin() ; j != mAtlasMap[i].end() ; ++j)
|
|
{
|
|
*j = NULL ;
|
|
}
|
|
for(ll_texture_atlas_list_t::iterator j = mEmptyAtlasMap[i].begin() ; j != mEmptyAtlasMap[i].end() ; ++j)
|
|
{
|
|
*j = NULL ;
|
|
}
|
|
|
|
mAtlasMap[i].clear() ;
|
|
mEmptyAtlasMap[i].clear() ;
|
|
}
|
|
mAtlasMap.clear() ;
|
|
mEmptyAtlasMap.clear() ;
|
|
}
|
|
|
|
//return TRUE if qualified
|
|
BOOL LLTextureAtlasManager::canAddToAtlas(S32 w, S32 h, S8 ncomponents, LLGLenum target)
|
|
{
|
|
if(ncomponents < 1 || ncomponents > 4)
|
|
{
|
|
return FALSE ;
|
|
}
|
|
//only support GL_TEXTURE_2D
|
|
if(GL_TEXTURE_2D != target)
|
|
{
|
|
return FALSE ;
|
|
}
|
|
//real image size overflows
|
|
if(w < 8 || w > LLTextureAtlas::sMaxSubTextureSize || h < 8 || h > LLTextureAtlas::sMaxSubTextureSize)
|
|
{
|
|
return FALSE ;
|
|
}
|
|
|
|
//if non-power-of-two number
|
|
if((w & (w - 1)) || (h & (h - 1)))
|
|
{
|
|
return FALSE ;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
void LLTextureAtlasManager::releaseAtlas(LLTextureAtlas* atlasp)
|
|
{
|
|
LLSpatialGroup* groupp = atlasp->getLastSpatialGroup() ;
|
|
while(groupp)
|
|
{
|
|
groupp->removeAtlas(atlasp, FALSE) ;
|
|
atlasp->removeLastSpatialGroup() ;
|
|
|
|
groupp = atlasp->getLastSpatialGroup() ;
|
|
}
|
|
|
|
S8 type = atlasp->getComponents() - 1 ;
|
|
//insert to the empty list
|
|
if(mEmptyAtlasMap[type].size() < MAX_NUM_EMPTY_ATLAS)
|
|
{
|
|
mEmptyAtlasMap[type].push_back(atlasp) ;
|
|
}
|
|
|
|
//delete the atlasp
|
|
mAtlasMap[type].remove(atlasp) ;
|
|
}
|
|
|
|
//
|
|
//this function reserves an appropriate slot from atlas pool for an image.
|
|
//return non-NULL if succeeds.
|
|
//Note:
|
|
//1, this function does not check if the image this slot assigned for qualifies for atlas or not,
|
|
// call LLTextureAtlasManager::canAddToAtlas(...) to do the check before calling this function.
|
|
//2, this function also dose not check if the image is already in atlas. It always assigns a new slot anyway.
|
|
//3, this function tries to group sub-textures from same spatial group into ONE atlas to improve render batching.
|
|
//
|
|
LLPointer<LLTextureAtlasSlot> LLTextureAtlasManager::reserveAtlasSlot(S32 sub_texture_size, S8 ncomponents,
|
|
LLSpatialGroup* groupp, LLViewerTexture* imagep)
|
|
{
|
|
if(!groupp)
|
|
{
|
|
//do not insert to atlas if does not have a group.
|
|
return NULL ;
|
|
}
|
|
|
|
//bits_len must <= 8 and is a power of two number, i.e.: must be one of these numbers: 1, 2, 4, 8.
|
|
if(sub_texture_size > LLTextureAtlas::sMaxSubTextureSize)
|
|
{
|
|
sub_texture_size = LLTextureAtlas::sMaxSubTextureSize ;
|
|
}
|
|
S8 bits_len = sub_texture_size / LLTextureAtlas::sSlotSize ;
|
|
if(bits_len < 1)
|
|
{
|
|
bits_len = 1 ;
|
|
}
|
|
|
|
S16 col = -1, row = -1;
|
|
S8 total_bits = bits_len * bits_len ;
|
|
|
|
//insert to the atlas reserved by the same spatial group
|
|
LLPointer<LLTextureAtlas> atlasp = groupp->getAtlas(ncomponents, total_bits) ;
|
|
if(atlasp.notNull())
|
|
{
|
|
if(!atlasp->getNextAvailableSlot(bits_len, col, row))
|
|
{
|
|
//failed
|
|
atlasp = NULL ;
|
|
}
|
|
}
|
|
|
|
//search an atlas to fit for 'size'
|
|
if(!atlasp)
|
|
{
|
|
S8 atlas_index = ncomponents - 1 ;
|
|
ll_texture_atlas_list_t::iterator iter = mAtlasMap[atlas_index].begin() ;
|
|
for(; iter != mAtlasMap[atlas_index].end(); ++iter)
|
|
{
|
|
LLTextureAtlas* cur = (LLTextureAtlas*)*iter ;
|
|
if(cur->getFullness() < MIN_ATLAS_FULLNESS)//this atlas is empty enough for this group to insert more sub-textures later if necessary.
|
|
{
|
|
if(cur->getNextAvailableSlot(bits_len, col, row))
|
|
{
|
|
atlasp = cur ;
|
|
groupp->addAtlas(atlasp) ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//create a new atlas if necessary
|
|
if(!atlasp)
|
|
{
|
|
if(mEmptyAtlasMap[ncomponents - 1].size() > 0)
|
|
{
|
|
//there is an empty one
|
|
atlasp = mEmptyAtlasMap[ncomponents - 1].back() ;
|
|
mEmptyAtlasMap[ncomponents - 1].pop_back() ;
|
|
}
|
|
else
|
|
{
|
|
atlasp = new LLTextureAtlas(ncomponents, 16) ;
|
|
}
|
|
mAtlasMap[ncomponents - 1].push_back(atlasp) ;
|
|
atlasp->getNextAvailableSlot(bits_len, col, row) ;
|
|
groupp->addAtlas(atlasp) ;
|
|
}
|
|
|
|
F32 xoffset, yoffset ;
|
|
atlasp->getTexCoordOffset(col, row, xoffset, yoffset) ;
|
|
LLPointer<LLTextureAtlasSlot> slot_infop = new LLTextureAtlasSlot(atlasp, groupp, col, row, xoffset, yoffset, bits_len) ;
|
|
|
|
return slot_infop ;
|
|
}
|
|
|
|
//*********************************************************************************************
|
|
//END of implementation of class LLTextureAtlasManager
|
|
//*********************************************************************************************
|