From bb0329d01216c15ea27ee03e713f9a451f9d53bd Mon Sep 17 00:00:00 2001 From: Nicky Date: Wed, 7 Jan 2015 01:34:44 +0100 Subject: [PATCH] Add debug floater to show how much texture memory objects in view use (per object). --- indra/llrender/llgltexture.h | 5 +- indra/newview/CMakeLists.txt | 2 + indra/newview/fsfloatervramusage.cpp | 343 ++++++++++++++++++ indra/newview/fsfloatervramusage.h | 61 ++++ indra/newview/llface.cpp | 17 + indra/newview/llselectmgr.cpp | 15 + indra/newview/llselectmgr.h | 47 ++- indra/newview/llviewerfloaterreg.cpp | 5 + .../default/xui/en/fs_floater_vram_usage.xml | 73 ++++ .../skins/default/xui/en/menu_viewer.xml | 7 + 10 files changed, 573 insertions(+), 2 deletions(-) create mode 100644 indra/newview/fsfloatervramusage.cpp create mode 100644 indra/newview/fsfloatervramusage.h create mode 100644 indra/newview/skins/default/xui/en/fs_floater_vram_usage.xml diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 45592ee077..a62b24c0ab 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -192,7 +192,10 @@ protected: protected: LLGLTextureState mTextureState ; - +// ND> Expose mipmap generation so we can check it for texture memory tax +public: + bool getUseMipMaps() const { return mUseMipMaps; } +// }; #endif // LL_GL_TEXTURE_H diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 02859310c6..f906f5659a 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -208,6 +208,7 @@ set(viewer_SOURCE_FILES lggcontactsets.cpp lfsimfeaturehandler.cpp llpanelopenregionsettings.cpp + fsfloatervramusage.cpp llaccountingcostmanager.cpp llagent.cpp @@ -926,6 +927,7 @@ set(viewer_HEADER_FILES lggbeamscolors.h lggcontactsets.h lfsimfeaturehandler.h + fsfloatervramusage.h llaccountingcostmanager.h llagent.h diff --git a/indra/newview/fsfloatervramusage.cpp b/indra/newview/fsfloatervramusage.cpp new file mode 100644 index 0000000000..3e6d0df0c8 --- /dev/null +++ b/indra/newview/fsfloatervramusage.cpp @@ -0,0 +1,343 @@ +/** + * $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 "llfloaterreg.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(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 = 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( aObject->getNumVertices() ) ); + item.columns.add().column("indices").value( static_cast( 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( size[0]*100 ); + S32 cmY = static_cast( size[1]*100 ); + S32 cmZ = static_cast( 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 ); +} diff --git a/indra/newview/fsfloatervramusage.h b/indra/newview/fsfloatervramusage.h new file mode 100644 index 0000000000..19984dc0cd --- /dev/null +++ b/indra/newview/fsfloatervramusage.h @@ -0,0 +1,61 @@ +/** + * $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$ + */ + +#ifndef FS_FLOATERVRAMUSAGE_H +#define FS_FLOATERVRAMUSAGE_H + +#include "llfloater.h" +#include "llselectmgr.h" + +class LLScrollListCtrl; +class LLViewerObject; +class LLFace; +class LLVertexBuffer; + +class FSFloaterVRAMUsage : public LLFloater, public nd::selection::PropertiesListener +{ +public: + FSFloaterVRAMUsage(const LLSD& seed); + /*virtual*/ ~FSFloaterVRAMUsage(); + /*virtual*/ void onOpen(const LLSD& key); + + BOOL postBuild(); + + virtual void onProperties( LLSelectNode const * ); + + void onIdle(); + +private: + void doRefresh(); + + void addObjectToList( LLViewerObject*, std::string const& ); + void calcFaceSize( LLFace *aFace, S32 &aW, S32 &aH ); + S32 calcVBOEntrySize( LLVertexBuffer *aVBO ); + U32 calcTexturSize( LLViewerObject*, std::ostream * = 0 ); + + struct ImplData; + ImplData *mData; +}; + +#endif // FS_FLOATERBLOCKLIST_H diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 10fd4a9c47..0250a871c3 100755 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -954,6 +954,23 @@ LLVector2 LLFace::surfaceToTexture(LLVector2 surface_coord, const LLVector4a& po void LLFace::getPlanarProjectedParams(LLQuaternion* face_rot, LLVector3* face_pos, F32* scale) const { const LLMatrix4& vol_mat = getWorldMatrix(); + if( ! getViewerObject() ) + { + LL_WARNS() << "No viewer object" << LL_ENDL; + return; + } + if( ! getViewerObject()->getVolume() ) + { + LL_WARNS() << "No volume" << LL_ENDL; + return; + } + + if( getViewerObject()->getVolume()->getNumVolumeFaces() <= mTEOffset ) + { + LL_WARNS() << "No volume face" << (S32)mTEOffset << LL_ENDL; + return; + } + const LLVolumeFace& vf = getViewerObject()->getVolume()->getVolumeFace(mTEOffset); const LLVector4a& normal4a = vf.mNormals[0]; const LLVector4a& tangent = vf.mTangents[0]; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index cc47e394bc..10a876f585 100755 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -490,6 +490,9 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(const std::vectoraddThisAndNonJointChildren(objects); addAsFamily(objects); + if( isBatchMode() ) + continue; + // Stop the object from moving (this anticipates changes on the // simulator in LLTask::userSelect) object->setVelocity(LLVector3::zero); @@ -498,6 +501,14 @@ LLObjectSelectionHandle LLSelectMgr::selectObjectAndFamily(const std::vectorresetRot(); } + if( isBatchMode() ) + { + mShowSelection = FALSE; + sendSelect(); + return mSelectedObjects; + } + + updateSelectionCenter(); saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK); updatePointAt(); @@ -5343,6 +5354,10 @@ void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data node->mInventorySerial = inv_serial; node->mSitName.assign(sit_name); node->mTouchName.assign(touch_name); + + // Fire for any observer interested in object properties + LLSelectMgr::instance().firePropertyReceived( node ); + // } } diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 417c24dd74..ed24549961 100755 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -234,6 +234,11 @@ protected: LLPointer mObject; S32 mTESelectMask; S32 mLastTESelected; + +// For const access. Need to check for isDead yourself. +public: + LLViewerObject const* getObject() const { return mObject; } +// }; class LLObjectSelection : public LLRefCount @@ -397,7 +402,47 @@ extern template class LLSelectMgr* LLSingleton::getInstance() // For use with getFirstTest() struct LLSelectGetFirstTest; -class LLSelectMgr : public LLEditMenuHandler, public LLSingleton +// To listened into received prop. messages +namespace nd +{ + namespace selection + { + class PropertiesListener + { + public: + virtual void onProperties( LLSelectNode const * ) = 0; + }; + + class PropertiesServer + { + public: + PropertiesServer() + : mBatchMode( false ) + { } + + void registerPropertyListener( nd::selection::PropertiesListener *aP) { mListener.insert( aP ); } + void removePropertyListener( nd::selection::PropertiesListener *aP) { mListener.erase( aP ); } + + void enableBatchMode( ) { mBatchMode = true; } + void disableBatchMode( ) { mBatchMode = false; } + bool isBatchMode() const { return mBatchMode; } + + protected: + void firePropertyReceived( LLSelectNode const *aNode ) + { + for( std::set< nd::selection::PropertiesListener * >::iterator itr = mListener.begin(); itr != mListener.end(); ++itr ) + (*itr)->onProperties( aNode ); + } + + private: + std::set< nd::selection::PropertiesListener * > mListener; + bool mBatchMode; + }; + } +} +// + +class LLSelectMgr : public LLEditMenuHandler, public LLSingleton, public nd::selection::PropertiesServer { public: static BOOL sRectSelectInclusive; // do we need to surround an object to pick it? diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index da47c5c449..47d7e658cc 100755 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -187,6 +187,9 @@ #include "NACLfloaterexploresounds.h" #include "particleeditor.h" #include "quickprefs.h" + +#include "fsfloatervramusage.h" + // handle secondlife:///app/openfloater/{NAME} URLs class LLFloaterOpenHandler : public LLCommandHandler { @@ -427,5 +430,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("sound_explorer", "floater_NACL_explore_sounds.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("ws_asset_blacklist", "floater_asset_blacklist.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add( "vram_usage", "fs_floater_vram_usage.xml", static_cast( &LLFloaterReg::build< FSFloaterVRAMUsage >) ); + LLFloaterReg::registerControlVariables(); // Make sure visibility and rect controls get preserved when saving } diff --git a/indra/newview/skins/default/xui/en/fs_floater_vram_usage.xml b/indra/newview/skins/default/xui/en/fs_floater_vram_usage.xml new file mode 100644 index 0000000000..63179d7aa6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/fs_floater_vram_usage.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 4868a0b65e..4728816925 100755 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3287,6 +3287,13 @@ function="ToggleControl" parameter="DebugShowTextureInfo" /> + + +