/** * @file llmemoryview.cpp * @brief LLMemoryView class implementation * * $LicenseInfo:firstyear=2001&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 "llmemoryview.h" #include "llappviewer.h" #include "llallocator_heap_profile.h" #include "llgl.h" // LLGLSUIDefault #include "llviewerwindow.h" #include "llviewercontrol.h" #include <sstream> #include <boost/algorithm/string/split.hpp> #include "llmemory.h" LLMemoryView::LLMemoryView(const LLMemoryView::Params& p) : LLView(p), mPaused(FALSE), //mDelay(120), mAlloc(NULL) { } LLMemoryView::~LLMemoryView() { } BOOL LLMemoryView::handleMouseDown(S32 x, S32 y, MASK mask) { if (mask & MASK_SHIFT) { } else if (mask & MASK_CONTROL) { } else { mPaused = !mPaused; } return TRUE; } BOOL LLMemoryView::handleMouseUp(S32 x, S32 y, MASK mask) { return TRUE; } BOOL LLMemoryView::handleHover(S32 x, S32 y, MASK mask) { return FALSE; } void LLMemoryView::refreshProfile() { /* LLAllocator & alloc = LLAppViewer::instance()->getAllocator(); if(alloc.isProfiling()) { std::string profile_text = alloc.getRawProfile(); boost::algorithm::split(mLines, profile_text, boost::bind(std::equal_to<llwchar>(), '\n', _1)); } else { mLines.clear(); } */ if (mAlloc == NULL) { mAlloc = &LLAppViewer::instance()->getAllocator(); } mLines.clear(); if(mAlloc->isProfiling()) { const LLAllocatorHeapProfile &prof = mAlloc->getProfile(); for(size_t i = 0; i < prof.mLines.size(); ++i) { std::stringstream ss; ss << "Unfreed Mem: " << (prof.mLines[i].mLiveSize >> 20) << " M Trace: "; for(size_t k = 0; k < prof.mLines[i].mTrace.size(); ++k) { ss << LLMemType::getNameFromID(prof.mLines[i].mTrace[k]) << " "; } mLines.push_back(utf8string_to_wstring(ss.str())); } } } void LLMemoryView::draw() { const S32 UPDATE_INTERVAL = 60; const S32 MARGIN_AMT = 10; static S32 curUpdate = UPDATE_INTERVAL; static LLUIColor s_console_color = LLUIColorTable::instance().getColor("ConsoleBackground", LLColor4U::black); // setup update interval if (curUpdate >= UPDATE_INTERVAL) { refreshProfile(); curUpdate = 0; } curUpdate++; // setup window properly S32 height = (S32) (gViewerWindow->getWindowRectScaled().getHeight()*0.75f); S32 width = (S32) (gViewerWindow->getWindowRectScaled().getWidth() * 0.9f); setRect(LLRect().setLeftTopAndSize(getRect().mLeft, getRect().mTop, width, height)); // setup window color F32 console_opacity = llclamp(gSavedSettings.getF32("ConsoleBackgroundOpacity"), 0.f, 1.f); LLColor4 color = s_console_color; color.mV[VALPHA] *= console_opacity; LLGLSUIDefault gls_ui; gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gl_rect_2d(0, height, width, 0, color); LLFontGL * font = LLFontGL::getFontSansSerifSmall(); // draw remaining lines F32 y_pos = 0.f; F32 y_off = 0.f; F32 line_height = font->getLineHeight(); S32 target_width = width - 2 * MARGIN_AMT; // cut off lines on bottom U32 max_lines = U32((height - 2 * line_height) / line_height); y_pos = height - MARGIN_AMT - line_height; y_off = 0.f; #if !MEM_TRACK_MEM std::vector<LLWString>::const_iterator end = mLines.end(); if(mLines.size() > max_lines) { end = mLines.begin() + max_lines; } for (std::vector<LLWString>::const_iterator i = mLines.begin(); i != end; ++i) { font->render(*i, 0, MARGIN_AMT, y_pos - y_off, LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, S32_MAX, target_width ); y_off += line_height; } #else LLMemTracker::getInstance()->preDraw(mPaused) ; { F32 x_pos = MARGIN_AMT ; U32 lines = 0 ; const char* str = LLMemTracker::getInstance()->getNextLine() ; while(str != NULL) { lines++ ; font->renderUTF8(str, 0, x_pos, y_pos - y_off, LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, S32_MAX, target_width, NULL, FALSE); str = LLMemTracker::getInstance()->getNextLine() ; y_off += line_height; if(lines >= max_lines) { lines = 0 ; x_pos += 512.f ; if(x_pos + 512.f > target_width) { break ; } y_pos = height - MARGIN_AMT - line_height; y_off = 0.f; } } } LLMemTracker::getInstance()->postDraw() ; #endif #if MEM_TRACK_TYPE S32 left, top, right, bottom; S32 x, y; S32 margin = 10; S32 texth = (S32)LLFontGL::getFontMonospace()->getLineHeight(); S32 xleft = margin; S32 ytop = height - margin; S32 labelwidth = 0; S32 maxmaxbytes = 1; // Make sure all timers are accounted for // Set 'MT_OTHER' to unaccounted ticks last frame { S32 display_memtypes[LLMemType::MTYPE_NUM_TYPES]; for (S32 i=0; i < LLMemType::MTYPE_NUM_TYPES; i++) { display_memtypes[i] = 0; } for (S32 i=0; i < MTV_DISPLAY_NUM; i++) { S32 tidx = mtv_display_table[i].memtype; display_memtypes[tidx]++; } LLMemType::sMemCount[LLMemType::MTYPE_OTHER] = 0; LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] = 0; for (S32 tidx = 0; tidx < LLMemType::MTYPE_NUM_TYPES; tidx++) { if (display_memtypes[tidx] == 0) { LLMemType::sMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMemCount[tidx]; LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMaxMemCount[tidx]; } } } // Labels { y = ytop; S32 peak = 0; for (S32 i=0; i<MTV_DISPLAY_NUM; i++) { x = xleft; int tidx = mtv_display_table[i].memtype; S32 bytes = LLMemType::sMemCount[tidx]; S32 maxbytes = LLMemType::sMaxMemCount[tidx]; maxmaxbytes = llmax(maxbytes, maxmaxbytes); peak += maxbytes; S32 mbytes = bytes >> 20; tdesc = llformat("%s [%4d MB] in %06d NEWS",mtv_display_table[i].desc,mbytes, LLMemType::sNewCount[tidx]); LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP); y -= (texth + 2); S32 textw = LLFontGL::getFontMonospace()->getWidth(tdesc); if (textw > labelwidth) labelwidth = textw; } S32 num_avatars = 0; S32 num_motions = 0; S32 num_loading_motions = 0; S32 num_loaded_motions = 0; S32 num_active_motions = 0; S32 num_deprecated_motions = 0; for (std::vector<LLCharacter*>::iterator iter = LLCharacter::sInstances.begin(); iter != LLCharacter::sInstances.end(); ++iter) { num_avatars++; (*iter)->getMotionController().incMotionCounts(num_motions, num_loading_motions, num_loaded_motions, num_active_motions, num_deprecated_motions); } x = xleft; tdesc = llformat("Total Bytes: %d MB Overhead: %d KB Avs %d Motions:%d Loading:%d Loaded:%d Active:%d Dep:%d", LLMemType::sTotalMem >> 20, LLMemType::sOverheadMem >> 10, num_avatars, num_motions, num_loading_motions, num_loaded_motions, num_active_motions, num_deprecated_motions); LLFontGL::getFontMonospace()->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP); } // Bars y = ytop; labelwidth += 8; S32 barw = width - labelwidth - xleft - margin; for (S32 i=0; i<MTV_DISPLAY_NUM; i++) { x = xleft + labelwidth; int tidx = mtv_display_table[i].memtype; S32 bytes = LLMemType::sMemCount[tidx]; F32 frac = (F32)bytes / (F32)maxmaxbytes; S32 w = (S32)(frac * (F32)barw); left = x; right = x + w; top = y; bottom = y - texth; gl_rect_2d(left, top, right, bottom, *mtv_display_table[i].color); S32 maxbytes = LLMemType::sMaxMemCount[tidx]; F32 frac2 = (F32)maxbytes / (F32)maxmaxbytes; S32 w2 = (S32)(frac2 * (F32)barw); left = x + w + 1; right = x + w2; top = y; bottom = y - texth; LLColor4 tcolor = *mtv_display_table[i].color; tcolor.setAlpha(.5f); gl_rect_2d(left, top, right, bottom, tcolor); y -= (texth + 2); } dumpData(); #endif LLView::draw(); }