/** * @file llvolumemgr.cpp * * $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 "linden_common.h" #include "llmutex.h" #include "llvolumemgr.h" #include "llvolume.h" const F32 BASE_THRESHOLD = 0.03f; //static F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD, 2*BASE_THRESHOLD, 8*BASE_THRESHOLD, 100*BASE_THRESHOLD}; //static F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f}; //============================================================================ LLVolumeMgr::LLVolumeMgr() : mDataMutex(NULL) { // the LLMutex magic interferes with easy unit testing, // so you now must manually call useMutex() to use it //mDataMutex = new LLMutex(); } LLVolumeMgr::~LLVolumeMgr() { cleanup(); delete mDataMutex; mDataMutex = NULL; } bool LLVolumeMgr::cleanup() { bool no_refs = true; if (mDataMutex) { mDataMutex->lock(); } for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), end = mVolumeLODGroups.end(); iter != end; iter++) { LLVolumeLODGroup *volgroupp = iter->second; if (!volgroupp->cleanupRefs()) { no_refs = false; } delete volgroupp; } mVolumeLODGroups.clear(); if (mDataMutex) { mDataMutex->unlock(); } return no_refs; } // Always only ever store the results of refVolume in a LLPointer // Note however that LLVolumeLODGroup that contains the volume // also holds a LLPointer so the volume will only go away after // anything holding the volume and the LODGroup are destroyed LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod) { LLVolumeLODGroup* volgroupp; if (mDataMutex) { mDataMutex->lock(); } volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params); if( iter == mVolumeLODGroups.end() ) { volgroupp = createNewGroup(volume_params); } else { volgroupp = iter->second; } if (mDataMutex) { mDataMutex->unlock(); } return volgroupp->refLOD(lod); } // virtual LLVolumeLODGroup* LLVolumeMgr::getGroup( const LLVolumeParams& volume_params ) const { LLVolumeLODGroup* volgroupp = NULL; if (mDataMutex) { mDataMutex->lock(); } volume_lod_group_map_t::const_iterator iter = mVolumeLODGroups.find(&volume_params); if( iter != mVolumeLODGroups.end() ) { volgroupp = iter->second; } if (mDataMutex) { mDataMutex->unlock(); } return volgroupp; } void LLVolumeMgr::unrefVolume(LLVolume *volumep) { if (volumep->isUnique()) { // TomY: Don't need to manage this volume. It is a unique instance. return; } const LLVolumeParams* params = &(volumep->getParams()); if (mDataMutex) { mDataMutex->lock(); } volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params); if( iter == mVolumeLODGroups.end() ) { LL_ERRS() << "Warning! Tried to cleanup unknown volume type! " << *params << LL_ENDL; if (mDataMutex) { mDataMutex->unlock(); } return; } else { LLVolumeLODGroup* volgroupp = iter->second; volgroupp->derefLOD(volumep); if (volgroupp->getNumRefs() == 0) { mVolumeLODGroups.erase(params); delete volgroupp; } } if (mDataMutex) { mDataMutex->unlock(); } } // protected void LLVolumeMgr::insertGroup(LLVolumeLODGroup* volgroup) { mVolumeLODGroups[volgroup->getVolumeParams()] = volgroup; } // protected LLVolumeLODGroup* LLVolumeMgr::createNewGroup(const LLVolumeParams& volume_params) { LLVolumeLODGroup* volgroup = new LLVolumeLODGroup(volume_params); insertGroup(volgroup); return volgroup; } // virtual void LLVolumeMgr::dump() { F32 avg = 0.f; if (mDataMutex) { mDataMutex->lock(); } for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(), end = mVolumeLODGroups.end(); iter != end; iter++) { LLVolumeLODGroup *volgroupp = iter->second; avg += volgroupp->dump(); } int count = (int)mVolumeLODGroups.size(); avg = count ? avg / (F32)count : 0.0f; if (mDataMutex) { mDataMutex->unlock(); } LL_INFOS() << "Average usage of LODs " << avg << LL_ENDL; } void LLVolumeMgr::useMutex() { if (!mDataMutex) { mDataMutex = new LLMutex(); } } std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr) { s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", "; S32 total_refs = 0; if (volume_mgr.mDataMutex) { volume_mgr.mDataMutex->lock(); } for (LLVolumeMgr::volume_lod_group_map_t::const_iterator iter = volume_mgr.mVolumeLODGroups.begin(); iter != volume_mgr.mVolumeLODGroups.end(); ++iter) { LLVolumeLODGroup *volgroupp = iter->second; total_refs += volgroupp->getNumRefs(); s << ", " << (*volgroupp); } if (volume_mgr.mDataMutex) { volume_mgr.mDataMutex->unlock(); } s << ", total_refs=" << total_refs << " }"; return s; } LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams ¶ms) : mVolumeParams(params), mRefs(0) { for (S32 i = 0; i < NUM_LODS; i++) { mLODRefs[i] = 0; mAccessCount[i] = 0; } } LLVolumeLODGroup::~LLVolumeLODGroup() { for (S32 i = 0; i < NUM_LODS; i++) { llassert_always(mLODRefs[i] == 0); } } // Called from LLVolumeMgr::cleanup bool LLVolumeLODGroup::cleanupRefs() { bool res = true; if (mRefs != 0) { LL_WARNS() << "Volume group has remaining refs:" << getNumRefs() << LL_ENDL; mRefs = 0; for (S32 i = 0; i < NUM_LODS; i++) { if (mLODRefs[i] > 0) { LL_WARNS() << " LOD " << i << " refs = " << mLODRefs[i] << LL_ENDL; mLODRefs[i] = 0; mVolumeLODs[i] = NULL; } } LL_WARNS() << *getVolumeParams() << LL_ENDL; res = false; } return res; } LLVolume* LLVolumeLODGroup::refLOD(const S32 lod) { llassert(lod >=0 && lod < NUM_LODS); mAccessCount[lod]++; mRefs++; if (mVolumeLODs[lod].isNull()) { mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]); } mLODRefs[lod]++; return mVolumeLODs[lod]; } bool LLVolumeLODGroup::derefLOD(LLVolume *volumep) { llassert_always(mRefs > 0); mRefs--; for (S32 i = 0; i < NUM_LODS; i++) { if (mVolumeLODs[i] == volumep) { llassert_always(mLODRefs[i] > 0); mLODRefs[i]--; #if 0 // SJB: Possible opt: keep other lods around if (!mLODRefs[i]) { mVolumeLODs[i] = NULL; } #endif return true; } } LL_ERRS() << "Deref of non-matching LOD in volume LOD group" << LL_ENDL; return false; } S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle) { S32 i = 0; while (i < (NUM_LODS - 1)) { if (tan_angle <= mDetailThresholds[i]) { return i; } i++; } return NUM_LODS - 1; } void LLVolumeLODGroup::getDetailProximity(const F32 tan_angle, F32 &to_lower, F32& to_higher) { S32 detail = getDetailFromTan(tan_angle); if (detail > 0) { to_lower = tan_angle - mDetailThresholds[detail]; } else { to_lower = 1024.f*1024.f; } if (detail < NUM_LODS-1) { to_higher = mDetailThresholds[detail+1] - tan_angle; } else { to_higher = 1024.f*1024.f; } } F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail) { return mDetailScales[detail]; } S32 LLVolumeLODGroup::getVolumeDetailFromScale(const F32 detail) { for (S32 i = 1; i < 4; i++) { if (mDetailScales[i] > detail) { return i-1; } } return 3; } F32 LLVolumeLODGroup::dump() { F32 usage = 0.f; for (S32 i = 0; i < NUM_LODS; i++) { if (mAccessCount[i] > 0) { usage += 1.f; } } usage = usage / (F32)NUM_LODS; std::string dump_str = llformat("%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]); LL_INFOS() << dump_str << LL_ENDL; return usage; } std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup) { s << "{ numRefs=" << volgroup.getNumRefs(); s << ", mParams=" << volgroup.getVolumeParams(); s << " }"; return s; }