343 lines
10 KiB
C++
343 lines
10 KiB
C++
/**
|
|
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
|
|
* Phoenix Firestorm Viewer Source Code
|
|
* Copyright (c) 2015 Nicky Dasmijn
|
|
*
|
|
* 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
|
|
*
|
|
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
|
|
* http://www.firestormviewer.org
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "fsfloatervramusage.h"
|
|
#include "llscrolllistctrl.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "lldrawable.h"
|
|
#include "llviewertexture.h"
|
|
#include "lltoolpie.h"
|
|
#include "llface.h"
|
|
#include "llvertexbuffer.h"
|
|
#include "llcallbacklist.h"
|
|
#include "llvoavatarself.h"
|
|
#include "llagentcamera.h"
|
|
|
|
const F32 PROPERTIES_REQUEST_TIMEOUT = 10.0f;
|
|
const F32 PROPERY_REQUEST_INTERVAL = 2.0f;
|
|
const U32 PROPERTIES_MAX_REQUEST_COUNT = 250;
|
|
|
|
static void onIdle( void *aData )
|
|
{
|
|
FSFloaterVRAMUsage *pFloater = reinterpret_cast<FSFloaterVRAMUsage*>(aData);
|
|
pFloater->onIdle();
|
|
}
|
|
|
|
struct ObjectStat
|
|
{
|
|
LLUUID mId;
|
|
U32 mTextureSize;
|
|
};
|
|
|
|
struct FSFloaterVRAMUsage::ImplData
|
|
{
|
|
LLScrollListCtrl *mList;
|
|
LLObjectSelectionHandle mSelection;
|
|
U32 mPending;
|
|
LLFrameTimer mPropTimer;
|
|
|
|
std::deque< ObjectStat > mObjects;
|
|
};
|
|
|
|
bool sortByTexture( ObjectStat const &lhs, ObjectStat const &rhs )
|
|
{
|
|
return lhs.mTextureSize > rhs.mTextureSize;
|
|
}
|
|
|
|
FSFloaterVRAMUsage::FSFloaterVRAMUsage(const LLSD& seed)
|
|
: LLFloater( seed )
|
|
{
|
|
mData = new ImplData();
|
|
mData->mList = 0;
|
|
mData->mPending = 0;
|
|
|
|
gIdleCallbacks.addFunction( &::onIdle, this) ;
|
|
}
|
|
|
|
FSFloaterVRAMUsage::~FSFloaterVRAMUsage()
|
|
{
|
|
gIdleCallbacks.deleteFunction( &::onIdle, this );
|
|
delete mData;
|
|
LLSelectMgr::instance().removePropertyListener( this );
|
|
LLSelectMgr::instance().enableSilhouette( true );
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::onOpen(const LLSD& key)
|
|
{
|
|
}
|
|
|
|
bool FSFloaterVRAMUsage::postBuild()
|
|
{
|
|
LLButton *pRefresh = getChild< LLButton >( "refresh_button" );
|
|
pRefresh->setClickedCallback( boost::bind( &FSFloaterVRAMUsage::doRefresh, this ) );
|
|
|
|
mData->mList = getChild< LLScrollListCtrl >( "result_list" );
|
|
|
|
LLSelectMgr::instance().registerPropertyListener( this );
|
|
LLSelectMgr::instance().enableSilhouette( false );
|
|
|
|
return true;
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::onIdle()
|
|
{
|
|
if( !mData->mPending && mData->mObjects.empty() )
|
|
{
|
|
LLSelectMgr::instance().deselectAll();
|
|
return;
|
|
}
|
|
|
|
if( mData->mPending && mData->mPropTimer.getElapsedTimeF32() < PROPERTIES_REQUEST_TIMEOUT )
|
|
return;
|
|
|
|
if( !mData->mPending && mData->mPropTimer.getStarted() && mData->mPropTimer.getElapsedTimeF32() < PROPERY_REQUEST_INTERVAL )
|
|
return;
|
|
|
|
LLSelectMgr::instance().deselectAll();
|
|
mData->mPending = 0;
|
|
|
|
std::vector< LLViewerObject* > vctSelection;
|
|
std::deque< ObjectStat > withoutRegion;
|
|
|
|
while( mData->mPending < PROPERTIES_MAX_REQUEST_COUNT && !mData->mObjects.empty() )
|
|
{
|
|
LLViewerObject *pObj = gObjectList.findObject( mData->mObjects[ 0 ].mId );
|
|
if( pObj && pObj->getRegion() )
|
|
{
|
|
++mData->mPending;
|
|
vctSelection.push_back( pObj );
|
|
}
|
|
else if( pObj )
|
|
withoutRegion.push_back( mData->mObjects[0] );
|
|
|
|
mData->mObjects.erase( mData->mObjects.begin() );
|
|
}
|
|
|
|
mData->mObjects.insert( mData->mObjects.end(), withoutRegion.begin(), withoutRegion.end() );
|
|
|
|
mData->mPropTimer.start();
|
|
if( vctSelection.size() )
|
|
{
|
|
LLSelectMgr::instance().enableBatchMode();
|
|
mData->mSelection = LLSelectMgr::instance().selectObjectAndFamily( vctSelection );
|
|
LLSelectMgr::instance().disableBatchMode();
|
|
}
|
|
}
|
|
|
|
U32 FSFloaterVRAMUsage::calcTexturSize( LLViewerObject *aObject, std::ostream *aTooltip )
|
|
{
|
|
if( !aObject || !aObject->mDrawable || aObject->mDrawable->isDead() )
|
|
return 0;
|
|
|
|
std::map< LLUUID, U32 > stTextures;
|
|
|
|
F64 totalTexSize = 0;
|
|
U8 numTEs = aObject->getNumTEs();
|
|
|
|
if (aTooltip )
|
|
*aTooltip << (U32)numTEs << " TEs" << std::endl;
|
|
|
|
for( U8 j = 0; j < numTEs; ++j )
|
|
{
|
|
LLViewerTexture *pTex = aObject->getTEImage( j );
|
|
if( !pTex || pTex->isMissingAsset() )
|
|
continue;
|
|
|
|
U32 textureId = static_cast<U32>(stTextures.size());
|
|
bool bOldTexId( false );
|
|
|
|
if( stTextures.end() != stTextures.find( pTex->getID() ) )
|
|
{
|
|
textureId = stTextures[ pTex->getID() ];
|
|
bOldTexId = true;
|
|
}
|
|
|
|
if (aTooltip )
|
|
*aTooltip << "TE: " << (U32)j << " tx: " << textureId << " w/h/c " << pTex->getFullWidth() << "/" << pTex->getFullHeight() << "/" << (U32)pTex->getComponents() << std::endl;
|
|
|
|
if( bOldTexId )
|
|
continue;
|
|
|
|
stTextures[ pTex->getID() ] = textureId;
|
|
|
|
S32 texSize = pTex->getFullWidth() * pTex->getFullHeight() * pTex->getComponents();
|
|
if( pTex->getUseMipMaps() )
|
|
texSize += (texSize*33)/100;
|
|
|
|
totalTexSize += texSize;
|
|
}
|
|
|
|
totalTexSize /= 1024.0;
|
|
return static_cast< U32 >( totalTexSize );
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::doRefresh()
|
|
{
|
|
mData->mList->deleteAllItems();
|
|
S32 numObjects = gObjectList.getNumObjects();
|
|
|
|
mData->mPending = 0;
|
|
mData->mObjects.clear();
|
|
LLSelectMgr::instance().deselectAll();
|
|
|
|
for( S32 i = 0; i < numObjects; ++i )
|
|
{
|
|
LLViewerObject *pObj = gObjectList.getObject( i );
|
|
if( !pObj ) // Might be dead
|
|
continue;
|
|
|
|
if( !pObj->mbCanSelect ||
|
|
pObj->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH ||
|
|
pObj->getPCode() == LLViewerObject::LL_VO_SKY ||
|
|
pObj->getPCode() == LLViewerObject::LL_VO_WL_SKY ||
|
|
pObj->getPCode() == LLViewerObject::LL_VO_VOID_WATER ||
|
|
pObj->getPCode() == LLViewerObject::LL_VO_WATER ||
|
|
pObj->isAvatar() )
|
|
continue;
|
|
|
|
// Exclude everything that's not in a sphere with r=draw distance around the avatar.
|
|
F64 distance = (pObj->getPositionGlobal() - gAgentAvatarp->getPositionGlobal()).length();
|
|
if( distance > gAgentCamera.mDrawDistance )
|
|
continue;
|
|
|
|
ObjectStat oObject;
|
|
oObject.mId = pObj->getID();
|
|
oObject.mTextureSize = calcTexturSize( pObj );
|
|
|
|
mData->mObjects.push_back( oObject );
|
|
}
|
|
std::sort( mData->mObjects.begin(), mData->mObjects.end(), sortByTexture );
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::addObjectToList( LLViewerObject *aObject, std::string const &aName )
|
|
{
|
|
LLScrollListItem::Params item;
|
|
|
|
F64 distance = (aObject->getPositionGlobal() - gAgentAvatarp->getPositionGlobal()).length();
|
|
|
|
item.columns.add().column("uuid").value( aObject->getID() );
|
|
item.columns.add().column("name").value( aName );
|
|
item.columns.add().column("distance").value( distance );
|
|
item.columns.add().column("faces").value( aObject->getNumFaces() );
|
|
item.columns.add().column("vertices").value( static_cast<S32>( aObject->getNumVertices() ) );
|
|
item.columns.add().column("indices").value( static_cast<S32>( aObject->getNumIndices() ) );
|
|
|
|
std::stringstream strTooltip;
|
|
U32 totalTexSize = calcTexturSize( aObject, &strTooltip );
|
|
|
|
F64 totalVboSize(0.0);
|
|
|
|
LLPointer< LLDrawable > pDrawable = aObject->mDrawable;
|
|
S32 numFaces = 0;
|
|
if( pDrawable && !pDrawable->isDead() )
|
|
numFaces = pDrawable->getNumFaces();
|
|
|
|
strTooltip << numFaces << " faces" << std::endl;
|
|
for (S32 j = 0; j < numFaces; j++)
|
|
{
|
|
LLFace* pFace = pDrawable->getFace( j );
|
|
if( !pFace )
|
|
continue;
|
|
|
|
S32 cmW = 0, cmH = 0;
|
|
|
|
calcFaceSize( pFace, cmW, cmH );
|
|
|
|
strTooltip << "Face: " << j << " extends (cm) w/h " << cmW << "/" << cmH << std::endl;
|
|
|
|
S32 vertexSize = calcVBOEntrySize( pFace->getVertexBuffer() ) * aObject->getNumVertices();;
|
|
S32 indexSize = sizeof( S16 ) * aObject->getNumIndices();
|
|
|
|
totalVboSize += vertexSize;
|
|
totalVboSize += indexSize;
|
|
}
|
|
|
|
totalVboSize /= 1024.0;
|
|
|
|
item.columns.add().column("vram_usage").value( (S32)totalTexSize );
|
|
item.columns.add().column("vram_usage_vbo").value( (S32)totalVboSize );
|
|
|
|
LLScrollListItem *pRow = mData->mList->addRow( item );
|
|
if( pRow )
|
|
{
|
|
for( S32 j = 0; j < pRow->getNumColumns(); ++j )
|
|
pRow->getColumn( j )->setToolTip( strTooltip.str() );
|
|
}
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::calcFaceSize( LLFace *aFace, S32 &aW, S32 &aH )
|
|
{
|
|
aW = aH = 0;
|
|
if( !aFace )
|
|
return;
|
|
|
|
LLVector4a size;
|
|
size.setSub( aFace->mExtents[1], aFace->mExtents[0] );
|
|
|
|
S32 cmX = static_cast<S32>( size[0]*100 );
|
|
S32 cmY = static_cast<S32>( size[1]*100 );
|
|
S32 cmZ = static_cast<S32>( size[2]*100 );
|
|
|
|
aW = cmX;
|
|
aH = cmY;
|
|
|
|
if( 0 != cmZ )
|
|
{
|
|
if( 0 == aW )
|
|
aW = cmZ;
|
|
else
|
|
aH = cmZ;
|
|
}
|
|
}
|
|
|
|
S32 FSFloaterVRAMUsage::calcVBOEntrySize( LLVertexBuffer *aVBO )
|
|
{
|
|
if( !aVBO )
|
|
return 0;
|
|
|
|
S32 vboEntrySize(0);
|
|
|
|
U32 vboMask = aVBO->getTypeMask();
|
|
for( S32 k = 0, l = 1; k < LLVertexBuffer::TYPE_MAX; ++k )
|
|
{
|
|
if( vboMask & l && k != LLVertexBuffer::TYPE_TEXTURE_INDEX )
|
|
vboEntrySize += LLVertexBuffer::sTypeSize[ k ];
|
|
l = l << 1;
|
|
}
|
|
|
|
return vboEntrySize;
|
|
}
|
|
|
|
void FSFloaterVRAMUsage::onProperties( LLSelectNode const *aProps )
|
|
{
|
|
if( !aProps || !aProps->getObject() )
|
|
return;
|
|
|
|
LLUUID id = aProps->getObject()->getID();
|
|
LLViewerObject *pObj = gObjectList.findObject( id );
|
|
addObjectToList( pObj, aProps->mName );
|
|
}
|