From 4a5ad357930f0bede4d84b9810978e9d0c5d268b Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 20 Jul 2012 11:42:15 -0500 Subject: MAINT-570 Remove unused memory tracking system LLMemType --- indra/newview/llappviewer.cpp | 6 ------ 1 file changed, 6 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 062551a3e8..ba2150e277 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1183,7 +1183,6 @@ static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE("Update"); bool LLAppViewer::mainLoop() { - LLMemType mt1(LLMemType::MTYPE_MAIN); mMainloopTimeout = new LLWatchdogTimeout(); //------------------------------------------- @@ -1283,7 +1282,6 @@ bool LLAppViewer::mainLoop() && (gHeadlessClient || !gViewerWindow->getShowProgress()) && !gFocusMgr.focusLocked()) { - LLMemType mjk(LLMemType::MTYPE_JOY_KEY); joystick->scanJoystick(); gKeyboard->scanKeyboard(); } @@ -1297,7 +1295,6 @@ bool LLAppViewer::mainLoop() if (gAres != NULL && gAres->isInitialized()) { - LLMemType mt_ip(LLMemType::MTYPE_IDLE_PUMP); pingMainloopTimeout("Main:ServicePump"); LLFastTimer t4(FTM_PUMP); { @@ -1347,7 +1344,6 @@ bool LLAppViewer::mainLoop() // Sleep and run background threads { - LLMemType mt_sleep(LLMemType::MTYPE_SLEEP); LLFastTimer t2(FTM_SLEEP); // yield some time to the os based on command line option @@ -4110,7 +4106,6 @@ static LLFastTimer::DeclareTimer FTM_VLMANAGER("VL Manager"); /////////////////////////////////////////////////////// void LLAppViewer::idle() { - LLMemType mt_idle(LLMemType::MTYPE_IDLE); pingMainloopTimeout("Main:Idle"); // Update frame timers @@ -4705,7 +4700,6 @@ static LLFastTimer::DeclareTimer FTM_CHECK_REGION_CIRCUIT("Check Region Circuit" void LLAppViewer::idleNetwork() { - LLMemType mt_in(LLMemType::MTYPE_IDLE_NETWORK); pingMainloopTimeout("idleNetwork"); gObjectList.mNumNewObjects = 0; -- cgit v1.3 From cb2b59b48b6cf8aeb17f5b0f9f4063e8340ce144 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Tue, 10 Jul 2012 00:42:10 +0000 Subject: Added new settings: DisableAllRenderTypes, DisableAllRenderFeatures, and MaxFPS. --- indra/newview/app_settings/settings.xml | 33 ++++++++++++++++++++++++++ indra/newview/llappviewer.cpp | 20 ++++++++++++++++ indra/newview/llappviewer.h | 1 + indra/newview/llviewerdisplay.cpp | 2 +- indra/newview/pipeline.cpp | 42 ++++++++++++++++++++++++++------- indra/newview/pipeline.h | 5 +++- 6 files changed, 93 insertions(+), 10 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5e50bd6e01..dde423f86c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8705,6 +8705,28 @@ Value 1 + DisableAllRenderTypes + + Comment + Disables all rendering types. + Persist + 0 + Type + Boolean + Value + 0 + + DisableAllRenderFeatures + + Comment + Disables all rendering features. + Persist + 0 + Type + Boolean + Value + 0 + RenderHUDInSnapshot Comment @@ -13010,6 +13032,17 @@ Value -1 + MaxFPS + + Comment + Yield some time to the local host if we reach a threshold framerate. + Persist + 1 + Type + F32 + Value + -1.0 + ZoomDirect Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index efa24796e5..f01f62c798 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -632,6 +632,7 @@ LLAppViewer::LLAppViewer() : mQuitRequested(false), mLogoutRequestSent(false), mYieldTime(-1), + mMinFrameTime(-1.0), mMainloopTimeout(NULL), mAgentRegionLastAlive(false), mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), @@ -1465,6 +1466,19 @@ bool LLAppViewer::mainLoop() { gFrameStalls++; } + + // Limit FPS + if (mMinFrameTime > F_APPROXIMATELY_ZERO) + { + // Sleep a while to limit frame rate. + S32 milliseconds_to_sleep = llclamp((S32)((mMinFrameTime - frameTimer.getElapsedTimeF64()) * 1000.f), 0, 1000); + if (milliseconds_to_sleep > 0) + { + LLFastTimer t(FTM_YIELD); + ms_sleep(milliseconds_to_sleep); + } + } + frameTimer.reset(); resumeMainloopTimeout(); @@ -2577,6 +2591,12 @@ bool LLAppViewer::initConfiguration() } mYieldTime = gSavedSettings.getS32("YieldTime"); + mMinFrameTime = -1.0f; + F32 max_fps = gSavedSettings.getF32("MaxFPS"); + if (max_fps > F_APPROXIMATELY_ZERO) + { + mMinFrameTime = 1.0f / max_fps; + } // Read skin/branding settings if specified. //if (! gDirUtilp->getSkinDir().empty() ) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index ae3c795d1e..f55954234f 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -254,6 +254,7 @@ private: bool mQuitRequested; // User wants to quit, may have modified documents open. bool mLogoutRequestSent; // Disconnect message sent to simulator, no longer safe to send messages to the sim. S32 mYieldTime; + F32 mMinFrameTime; struct SettingsFiles* mSettingsLocationList; LLWatchdogTimeout* mMainloopTimeout; diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 4571d08050..e4451c3c93 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -1042,7 +1042,7 @@ void render_hud_attachments() gPipeline.pushRenderTypeMask(); // turn off everything - gPipeline.andRenderTypeMask(LLPipeline::END_RENDER_TYPES); + gPipeline.clearAllRenderTypes(); // turn on HUD gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); // turn on HUD particles diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index de72d79043..cd4c7289a7 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -471,19 +471,29 @@ void LLPipeline::init() LLViewerStats::getInstance()->mTrianglesDrawnStat.reset(); resetFrameStats(); - for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) + if (gSavedSettings.getBOOL("DisableAllRenderFeatures")) { - mRenderTypeEnabled[i] = TRUE; //all rendering types start enabled + mRenderDebugFeatureMask = 0x0; + } + else + { + mRenderDebugFeatureMask = 0xffffffff; // By default, all debugging features on } - - mRenderDebugFeatureMask = 0xffffffff; // All debugging features on mRenderDebugMask = 0; // All debug starts off - // Don't turn on ground when this is set - // Mac Books with intel 950s need this - if(!gSavedSettings.getBOOL("RenderGround")) + if (gSavedSettings.getBOOL("DisableAllRenderTypes")) { - toggleRenderType(RENDER_TYPE_GROUND); + clearAllRenderTypes(); + } + else + { + setAllRenderTypes(); // By default, all rendering types start enabled + // Don't turn on ground when this is set + // Mac Books with intel 950s need this + if(!gSavedSettings.getBOOL("RenderGround")) + { + toggleRenderType(RENDER_TYPE_GROUND); + } } // make sure RenderPerformanceTest persists (hackity hack hack) @@ -9945,6 +9955,22 @@ void LLPipeline::clearRenderTypeMask(U32 type, ...) } } +void LLPipeline::setAllRenderTypes() +{ + for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) + { + mRenderTypeEnabled[i] = TRUE; + } +} + +void LLPipeline::clearAllRenderTypes() +{ + for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) + { + mRenderTypeEnabled[i] = FALSE; + } +} + void LLPipeline::addDebugBlip(const LLVector3& position, const LLColor4& color) { DebugBlip blip(position, color); diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 6ae482fa06..3536746eb1 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -311,9 +311,12 @@ public: BOOL hasAnyRenderType(const U32 type, ...) const; void setRenderTypeMask(U32 type, ...); - void orRenderTypeMask(U32 type, ...); + // This is equivalent to 'setRenderTypeMask' + //void orRenderTypeMask(U32 type, ...); void andRenderTypeMask(U32 type, ...); void clearRenderTypeMask(U32 type, ...); + void setAllRenderTypes(); + void clearAllRenderTypes(); void pushRenderTypeMask(); void popRenderTypeMask(); -- cgit v1.3 From a589bc99701d6258d3dd467a5d36fb989348a758 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Wed, 11 Jul 2012 00:30:04 +0000 Subject: Added LLPipeline eventhost API. Added ForcePeriodicRenderingTime setting. --- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings.xml | 11 ++ indra/newview/llappviewer.cpp | 47 ++++--- indra/newview/llappviewer.h | 2 - indra/newview/llpipelinelistener.cpp | 216 ++++++++++++++++++++++++++++++++ indra/newview/llpipelinelistener.h | 41 ++++++ indra/newview/llviewermenu.h | 5 + indra/newview/pipeline.cpp | 25 +++- indra/newview/pipeline.h | 10 +- 9 files changed, 337 insertions(+), 22 deletions(-) create mode 100644 indra/newview/llpipelinelistener.cpp create mode 100644 indra/newview/llpipelinelistener.h (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9783601696..5a1055319a 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -418,6 +418,7 @@ set(viewer_SOURCE_FILES llpatchvertexarray.cpp llphysicsmotion.cpp llphysicsshapebuilderutil.cpp + llpipelinelistener.cpp llplacesinventorybridge.cpp llplacesinventorypanel.cpp llpopupview.cpp @@ -967,6 +968,7 @@ set(viewer_HEADER_FILES llpatchvertexarray.h llphysicsmotion.h llphysicsshapebuilderutil.h + llpipelinelistener.h llplacesinventorybridge.h llplacesinventorypanel.h llpolymesh.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index dde423f86c..8c82b761bb 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -13043,6 +13043,17 @@ Value -1.0 + ForcePeriodicRenderingTime + + Comment + Periodically enable all rendering masks for a single frame. + Persist + 1 + Type + F32 + Value + -1.0 + ZoomDirect Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f01f62c798..f5dce58758 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -631,8 +631,6 @@ LLAppViewer::LLAppViewer() : mForceGraphicsDetail(false), mQuitRequested(false), mLogoutRequestSent(false), - mYieldTime(-1), - mMinFrameTime(-1.0), mMainloopTimeout(NULL), mAgentRegionLastAlive(false), mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), @@ -1200,7 +1198,7 @@ bool LLAppViewer::mainLoop() LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true); - LLTimer frameTimer,idleTimer; + LLTimer frameTimer,idleTimer,periodicRenderingTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); joystick->setNeedsReset(true); @@ -1212,6 +1210,8 @@ bool LLAppViewer::mainLoop() // point of posting. LLSD newFrame; + BOOL restore_rendering_masks = FALSE; + //LLPrivateMemoryPoolTester::getInstance()->run(false) ; //LLPrivateMemoryPoolTester::getInstance()->run(true) ; //LLPrivateMemoryPoolTester::destroy() ; @@ -1229,6 +1229,28 @@ bool LLAppViewer::mainLoop() try { + // Check if we need to restore rendering masks. + if (restore_rendering_masks) + { + gPipeline.popRenderDebugFeatureMask(); + gPipeline.popRenderTypeMask(); + } + // Check if we need to temporarily enable rendering. + F32 periodic_rendering = gSavedSettings.getF32("ForcePeriodicRenderingTime"); + if (periodic_rendering > F_APPROXIMATELY_ZERO && periodicRenderingTimer.getElapsedTimeF64() > periodic_rendering) + { + periodicRenderingTimer.reset(); + restore_rendering_masks = TRUE; + gPipeline.pushRenderTypeMask(); + gPipeline.pushRenderDebugFeatureMask(); + gPipeline.setAllRenderTypes(); + gPipeline.setAllRenderDebugFeatures(); + } + else + { + restore_rendering_masks = FALSE; + } + pingMainloopTimeout("Main:MiscNativeWindowEvents"); if (gViewerWindow) @@ -1351,10 +1373,11 @@ bool LLAppViewer::mainLoop() LLFastTimer t2(FTM_SLEEP); // yield some time to the os based on command line option - if(mYieldTime >= 0) + S32 yield_time = gSavedSettings.getS32("YieldTime"); + if(yield_time >= 0) { LLFastTimer t(FTM_YIELD); - ms_sleep(mYieldTime); + ms_sleep(yield_time); } // yield cooperatively when not running as foreground window @@ -1468,10 +1491,12 @@ bool LLAppViewer::mainLoop() } // Limit FPS - if (mMinFrameTime > F_APPROXIMATELY_ZERO) + F32 max_fps = gSavedSettings.getF32("MaxFPS"); + if (max_fps > F_APPROXIMATELY_ZERO) { // Sleep a while to limit frame rate. - S32 milliseconds_to_sleep = llclamp((S32)((mMinFrameTime - frameTimer.getElapsedTimeF64()) * 1000.f), 0, 1000); + F32 min_frame_time = 1.f / max_fps; + S32 milliseconds_to_sleep = llclamp((S32)((min_frame_time - frameTimer.getElapsedTimeF64()) * 1000.f), 0, 1000); if (milliseconds_to_sleep > 0) { LLFastTimer t(FTM_YIELD); @@ -2590,14 +2615,6 @@ bool LLAppViewer::initConfiguration() } } - mYieldTime = gSavedSettings.getS32("YieldTime"); - mMinFrameTime = -1.0f; - F32 max_fps = gSavedSettings.getF32("MaxFPS"); - if (max_fps > F_APPROXIMATELY_ZERO) - { - mMinFrameTime = 1.0f / max_fps; - } - // Read skin/branding settings if specified. //if (! gDirUtilp->getSkinDir().empty() ) //{ diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index f55954234f..043893020b 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -253,8 +253,6 @@ private: bool mQuitRequested; // User wants to quit, may have modified documents open. bool mLogoutRequestSent; // Disconnect message sent to simulator, no longer safe to send messages to the sim. - S32 mYieldTime; - F32 mMinFrameTime; struct SettingsFiles* mSettingsLocationList; LLWatchdogTimeout* mMainloopTimeout; diff --git a/indra/newview/llpipelinelistener.cpp b/indra/newview/llpipelinelistener.cpp new file mode 100644 index 0000000000..20759239bf --- /dev/null +++ b/indra/newview/llpipelinelistener.cpp @@ -0,0 +1,216 @@ +/** + * @file llpipelinelistener.h + * @author Don Kjer + * @date 2012-07-09 + * @brief Implementation for LLPipelineListener + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" + +#include "llpipelinelistener.h" + +#include "pipeline.h" +#include "stringize.h" +#include +#include "llviewermenu.h" + + +namespace { + // Render Types + void toggle_render_types_wrapper(LLSD const& request) + { + for (LLSD::array_const_iterator iter = request["types"].beginArray(); + iter != request["types"].endArray(); + ++iter) + { + U32 render_type = render_type_from_string( iter->asString() ); + if ( render_type != 0 ) + { + LLPipeline::toggleRenderTypeControl( (void*) render_type ); + } + } + } + + void has_render_type_wrapper(LLSD const& request) + { + LLEventAPI::Response response(LLSD(), request); + U32 render_type = render_type_from_string( request["type"].asString() ); + if ( render_type != 0 ) + { + response["value"] = LLPipeline::hasRenderTypeControl( (void*) render_type ); + } + else + { + response.error(STRINGIZE("unknown type '" << request["type"].asString() << "'")); + } + } + + void disable_all_render_types_wrapper(LLSD const& request) + { + gPipeline.clearAllRenderTypes(); + } + + void enable_all_render_types_wrapper(LLSD const& request) + { + gPipeline.setAllRenderTypes(); + } + + // Render Features + void toggle_render_features_wrapper(LLSD const& request) + { + for (LLSD::array_const_iterator iter = request["features"].beginArray(); + iter != request["features"].endArray(); + ++iter) + { + U32 render_feature = feature_from_string( iter->asString() ); + if ( render_feature != 0 ) + { + LLPipeline::toggleRenderDebugControl( (void*) render_feature ); + } + } + } + + void has_render_feature_wrapper(LLSD const& request) + { + LLEventAPI::Response response(LLSD(), request); + U32 render_feature = feature_from_string( request["feature"].asString() ); + if ( render_feature != 0 ) + { + response["value"] = gPipeline.hasRenderDebugFeatureMask(render_feature); + } + else + { + response.error(STRINGIZE("unknown feature '" << request["feature"].asString() << "'")); + } + } + + void disable_all_render_features_wrapper(LLSD const& request) + { + gPipeline.clearAllRenderDebugFeatures(); + } + + void enable_all_render_features_wrapper(LLSD const& request) + { + gPipeline.setAllRenderDebugFeatures(); + } + + // Render Info Displays + void toggle_info_displays_wrapper(LLSD const& request) + { + for (LLSD::array_const_iterator iter = request["displays"].beginArray(); + iter != request["displays"].endArray(); + ++iter) + { + U32 info_display = info_display_from_string( iter->asString() ); + if ( info_display != 0 ) + { + LLPipeline::toggleRenderDebug( (void*) info_display ); + } + } + } + + void has_info_display_wrapper(LLSD const& request) + { + LLEventAPI::Response response(LLSD(), request); + U32 info_display = info_display_from_string( request["display"].asString() ); + if ( info_display != 0 ) + { + response["value"] = gPipeline.hasRenderDebugMask(info_display); + } + else + { + response.error(STRINGIZE("unknown display '" << request["display"].asString() << "'")); + } + } + + void disable_all_info_displays_wrapper(LLSD const& request) + { + gPipeline.clearAllRenderDebugDisplays(); + } + + void enable_all_info_displays_wrapper(LLSD const& request) + { + gPipeline.setAllRenderDebugDisplays(); + } + +} + + +LLPipelineListener::LLPipelineListener(): + LLEventAPI("LLPipeline", + "API to te rendering pipeline.") +{ + // Render Types + add("toggleRenderTypes", + "Toggle rendering [\"types\"]:\n" + "See: llviewermenu.cpp:render_type_from_string for list of available types.", + &toggle_render_types_wrapper); + add("hasRenderType", + "Check if rendering [\"type\"] is enabled:\n" + "See: llviewermenu.cpp:render_type_from_string for list of available types.", + &has_render_type_wrapper, + LLSDMap("reply", LLSD())); + add("disableAllRenderTypes", + "Turn off all rendering types.", + &disable_all_render_types_wrapper); + add("enableAllRenderTypes", + "Turn on all rendering types.", + &enable_all_render_types_wrapper); + + // Render Features + add("toggleRenderFeatures", + "Toggle rendering [\"features\"]:\n" + "See: llviewermenu.cpp:feature_from_string for list of available features.", + &toggle_render_features_wrapper); + add("hasRenderFeature", + "Check if rendering [\"feature\"] is enabled:\n" + "See: llviewermenu.cpp:render_feature_from_string for list of available features.", + &has_render_feature_wrapper, + LLSDMap("reply", LLSD())); + add("disableAllRenderFeatures", + "Turn off all rendering features.", + &disable_all_render_features_wrapper); + add("enableAllRenderFeatures", + "Turn on all rendering features.", + &enable_all_render_features_wrapper); + + // Render Info Displays + add("toggleRenderInfoDisplays", + "Toggle info [\"displays\"]:\n" + "See: llviewermenu.cpp:info_display_from_string for list of available displays.", + &toggle_info_displays_wrapper); + add("hasRenderInfoDisplay", + "Check if info [\"display\"] is enabled:\n" + "See: llviewermenu.cpp:info_display_from_string for list of available displays.", + &has_info_display_wrapper, + LLSDMap("reply", LLSD())); + add("disableAllRenderInfoDisplays", + "Turn off all info displays.", + &disable_all_info_displays_wrapper); + add("enableAllRenderInfoDisplays", + "Turn on all info displays.", + &enable_all_info_displays_wrapper); +} + diff --git a/indra/newview/llpipelinelistener.h b/indra/newview/llpipelinelistener.h new file mode 100644 index 0000000000..da1898e57b --- /dev/null +++ b/indra/newview/llpipelinelistener.h @@ -0,0 +1,41 @@ +/** + * @file llpipelinelistener.h + * @author Don Kjer + * @date 2012-07-09 + * @brief Wrap subset of LLPipeline API in event API + * + * $LicenseInfo:firstyear=2012&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$ + */ + +#if ! defined(LL_LLPIPELINELISTENER_H) +#define LL_LLPIPELINELISTENER_H + +#include "lleventapi.h" + +/// Listen on an LLEventPump with specified name for LLPipeline request events. +class LLPipelineListener: public LLEventAPI +{ +public: + LLPipelineListener(); +}; + +#endif /* ! defined(LL_LLPIPELINELISTENER_H) */ diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 8c40762865..c809ebde43 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -130,6 +130,11 @@ bool handle_go_to(); // Export to XML or Collada void handle_export_selected( void * ); +// Convert strings to internal types +U32 render_type_from_string(std::string render_type); +U32 feature_from_string(std::string feature); +U32 info_display_from_string(std::string info_display); + class LLViewerMenuHolderGL : public LLMenuHolderGL { public: diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index cd4c7289a7..72cc6bfcd4 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -72,6 +72,7 @@ #include "llhudtext.h" #include "lllightconstants.h" #include "llmeshrepository.h" +#include "llpipelinelistener.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llsky.h" @@ -368,6 +369,8 @@ BOOL LLPipeline::sMemAllocationThrottled = FALSE; S32 LLPipeline::sVisibleLightCount = 0; F32 LLPipeline::sMinRenderSize = 0.f; +// EventHost API LLPipeline listener. +static LLPipelineListener sPipelineListener; static LLCullResult* sCull = NULL; @@ -473,13 +476,13 @@ void LLPipeline::init() if (gSavedSettings.getBOOL("DisableAllRenderFeatures")) { - mRenderDebugFeatureMask = 0x0; + clearAllRenderDebugFeatures(); } else { - mRenderDebugFeatureMask = 0xffffffff; // By default, all debugging features on + setAllRenderDebugFeatures(); // By default, all debugging features on } - mRenderDebugMask = 0; // All debug starts off + clearAllRenderDebugDisplays(); // All debug displays off if (gSavedSettings.getBOOL("DisableAllRenderTypes")) { @@ -6008,6 +6011,22 @@ void LLPipeline::setRenderDebugFeatureControl(U32 bit, bool value) } } +void LLPipeline::pushRenderDebugFeatureMask() +{ + mRenderDebugFeatureStack.push(mRenderDebugFeatureMask); +} + +void LLPipeline::popRenderDebugFeatureMask() +{ + if (mRenderDebugFeatureStack.empty()) + { + llerrs << "Depleted render feature stack." << llendl; + } + + mRenderDebugFeatureMask = mRenderDebugFeatureStack.top(); + mRenderDebugFeatureStack.pop(); +} + // static void LLPipeline::setRenderScriptedBeacons(BOOL val) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 3536746eb1..16610b8c68 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -304,8 +304,10 @@ public: BOOL hasRenderDebugFeatureMask(const U32 mask) const { return (mRenderDebugFeatureMask & mask) ? TRUE : FALSE; } BOOL hasRenderDebugMask(const U32 mask) const { return (mRenderDebugMask & mask) ? TRUE : FALSE; } - - + void setAllRenderDebugFeatures() { mRenderDebugFeatureMask = 0xffffffff; } + void clearAllRenderDebugFeatures() { mRenderDebugFeatureMask = 0x0; } + void setAllRenderDebugDisplays() { mRenderDebugMask = 0xffffffff; } + void clearAllRenderDebugDisplays() { mRenderDebugMask = 0x0; } BOOL hasRenderType(const U32 type) const; BOOL hasAnyRenderType(const U32 type, ...) const; @@ -321,6 +323,9 @@ public: void pushRenderTypeMask(); void popRenderTypeMask(); + void pushRenderDebugFeatureMask(); + void popRenderDebugFeatureMask(); + static void toggleRenderType(U32 type); // For UI control of render features @@ -609,6 +614,7 @@ protected: U32 mRenderDebugFeatureMask; U32 mRenderDebugMask; + std::stack mRenderDebugFeatureStack; U32 mOldRenderDebugMask; -- cgit v1.3 From 5a473a42a63d3277c65bb3bc96e49f020508cfe8 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Fri, 13 Jul 2012 02:54:48 +0000 Subject: Remove MaxFPS limit during login, teleports, and logout. --- indra/newview/llappviewer.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f5dce58758..8210a41a78 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1492,7 +1492,12 @@ bool LLAppViewer::mainLoop() // Limit FPS F32 max_fps = gSavedSettings.getF32("MaxFPS"); - if (max_fps > F_APPROXIMATELY_ZERO) + // Only limit FPS when we are actually rendering something. Otherwise + // logins, logouts and teleports take much longer to complete. + if (max_fps > F_APPROXIMATELY_ZERO && + LLStartUp::getStartupState() == STATE_STARTED && + !gTeleportDisplay && + !logoutRequestSent()) { // Sleep a while to limit frame rate. F32 min_frame_time = 1.f / max_fps; -- cgit v1.3 From 5d32e23a11f272a2cdf8b6aac106534b6814ea98 Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Thu, 12 Jul 2012 22:35:51 -0700 Subject: SH-3275 WIP Run viewer metrics for object update messages improved update logging API and output format --- indra/newview/app_settings/logcontrol.xml | 20 +++- indra/newview/llappviewer.cpp | 8 -- indra/newview/llviewerobjectlist.cpp | 37 ++++-- indra/newview/llviewerregion.cpp | 6 +- indra/newview/llviewerstatsrecorder.cpp | 188 +++++++++++++++++------------- indra/newview/llviewerstatsrecorder.h | 37 +++--- 6 files changed, 174 insertions(+), 122 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 64122bbb6c..8636ba1090 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -48,6 +48,24 @@ --> - + + level + NONE + functions + + + classes + + LLViewerStatsRecorder + + files + + + tags + + + + + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index ed04b5bf38..c02bc882ea 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -719,10 +719,6 @@ bool LLAppViewer::init() mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); -#if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::initClass(); -#endif - // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1894,10 +1890,6 @@ bool LLAppViewer::cleanup() LLMetricPerformanceTesterBasic::cleanClass() ; -#if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::cleanupClass(); -#endif - llinfos << "Cleaning up Media and Textures" << llendflush; //Note: diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 54ccfb9aae..b010ac9aa7 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -334,6 +334,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, U64 region_handle; mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); + LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); if (!regionp) @@ -345,9 +346,9 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, U8 compressed_dpbuffer[2048]; LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048); LLDataPacker *cached_dpp = NULL; - + LLViewerStatsRecorder& recorder = LLViewerStatsRecorder::instance(); #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(regionp); + recorder.beginObjectUpdateEvents(1.f); #endif for (i = 0; i < num_objects; i++) @@ -355,6 +356,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, // timer is unused? LLTimer update_timer; BOOL justCreated = FALSE; + S32 msg_size = 0; if (cached) { @@ -362,6 +364,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, U32 crc; mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i); mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i); + msg_size += sizeof(U32) * 2; // Lookup data packer and add this id to cache miss lists if necessary. U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE; @@ -377,9 +380,9 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, else { // Cache Miss. - #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordCacheMissEvent(id, update_type, cache_miss_type); - #endif +#if LL_RECORD_VIEWER_STATS + recorder.recordCacheMissEvent(id, update_type, cache_miss_type, msg_size); +#endif continue; // no data packer, skip this object } @@ -391,10 +394,12 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, S32 compressed_length; compressed_dp.reset(); + U32 flags = 0; if (update_type != OUT_TERSE_IMPROVED) // OUT_FULL_COMPRESSED only? { mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i); + msg_size += sizeof(U32); } // I don't think we ever use this flag from the server. DK 2010/12/09 @@ -402,6 +407,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { //llinfos << "TEST: flags & FLAGS_ZLIB_COMPRESSED" << llendl; compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); + msg_size += compressed_length; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i); uncompressed_length = 2048; uncompress(compressed_dpbuffer, (unsigned long *)&uncompressed_length, @@ -411,6 +417,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, else { uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); + msg_size += uncompressed_length; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i); compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length); } @@ -439,6 +446,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, else if (update_type != OUT_FULL) // !compressed, !OUT_FULL ==> OUT_FULL_CACHED only? { mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); + msg_size += sizeof(U32); + getUUIDFromLocal(fullid, local_id, gMessageSystem->getSenderIP(), @@ -453,6 +462,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i); mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); + msg_size += sizeof(LLUUID); + msg_size += sizeof(U32); // llinfos << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << llendl; } objectp = findObject(fullid); @@ -499,7 +510,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { // llinfos << "terse update for an unknown object (compressed):" << fullid << llendl; #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); + recorder.recordObjectUpdateFailure(local_id, update_type, msg_size); #endif continue; } @@ -513,12 +524,14 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { //llinfos << "terse update for an unknown object:" << fullid << llendl; #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); + recorder.recordObjectUpdateFailure(local_id, update_type, msg_size); #endif continue; } mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i); + msg_size += sizeof(U8); + } #ifdef IGNORE_DEAD if (mDeadObjects.find(fullid) != mDeadObjects.end()) @@ -526,7 +539,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, mNumDeadObjectUpdates++; //llinfos << "update for a dead object:" << fullid << llendl; #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); + recorder.recordObjectUpdateFailure(local_id, update_type, msg_size); #endif continue; } @@ -537,7 +550,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { llinfos << "createObject failure for object: " << fullid << llendl; #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordObjectUpdateFailure(local_id, update_type); + recorder.recordObjectUpdateFailure(local_id, update_type, msg_size); #endif continue; } @@ -566,7 +579,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, bCached = true; #if LL_RECORD_VIEWER_STATS LLViewerRegion::eCacheUpdateResult result = objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp); - LLViewerStatsRecorder::instance()->recordCacheFullUpdate(local_id, update_type, result, objectp); + recorder.recordCacheFullUpdate(local_id, update_type, result, objectp, msg_size); #else objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp); #endif @@ -586,14 +599,14 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated); } #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->recordObjectUpdateEvent(local_id, update_type, objectp); + recorder.recordObjectUpdateEvent(local_id, update_type, objectp, msg_size); #endif objectp->setLastUpdateType(update_type); objectp->setLastUpdateCached(bCached); } #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->endObjectUpdateEvents(); + recorder.endObjectUpdateEvents(); #endif LLVOAvatar::cullAvatarsByPixelArea(); diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index e3cb985ddb..0e2927cea4 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1318,9 +1318,9 @@ void LLViewerRegion::requestCacheMisses() mCacheDirty = TRUE ; // llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl; #if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::instance()->beginObjectUpdateEvents(this); - LLViewerStatsRecorder::instance()->recordRequestCacheMissesEvent(full_count + crc_count); - LLViewerStatsRecorder::instance()->endObjectUpdateEvents(); + LLViewerStatsRecorder::instance().beginObjectUpdateEvents(1.f); + LLViewerStatsRecorder::instance().recordRequestCacheMissesEvent(full_count + crc_count); + LLViewerStatsRecorder::instance().endObjectUpdateEvents(); #endif } diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp index e9d21b4848..cfb446fe45 100644 --- a/indra/newview/llviewerstatsrecorder.cpp +++ b/indra/newview/llviewerstatsrecorder.cpp @@ -45,9 +45,10 @@ LLViewerStatsRecorder* LLViewerStatsRecorder::sInstance = NULL; LLViewerStatsRecorder::LLViewerStatsRecorder() : mObjectCacheFile(NULL), mTimer(), - mRegionp(NULL), - mStartTime(0.f), - mProcessingTime(0.f) + mStartTime(0.0), + mProcessingStartTime(0.0), + mProcessingTotalTime(0.0), + mLastSnapshotTime(0.0) { if (NULL != sInstance) { @@ -61,112 +62,114 @@ LLViewerStatsRecorder::~LLViewerStatsRecorder() { if (mObjectCacheFile != NULL) { + // last chance snapshot + takeSnapshot(); LLFile::close(mObjectCacheFile); mObjectCacheFile = NULL; } } -// static -void LLViewerStatsRecorder::initClass() -{ - sInstance = new LLViewerStatsRecorder(); -} - -// static -void LLViewerStatsRecorder::cleanupClass() -{ - delete sInstance; - sInstance = NULL; -} - - -void LLViewerStatsRecorder::initStatsRecorder(LLViewerRegion *regionp) +void LLViewerStatsRecorder::beginObjectUpdateEvents(F32 interval) { + mSnapshotInterval = interval; if (mObjectCacheFile == NULL) { - mStartTime = LLTimer::getTotalTime(); + mStartTime = LLTimer::getTotalSeconds(); mObjectCacheFile = LLFile::fopen(STATS_FILE_NAME, "wb"); if (mObjectCacheFile) { // Write column headers std::ostringstream data_msg; - data_msg << "EventTime, " - << "ProcessingTime, " - << "CacheHits, " - << "CacheFullMisses, " - << "CacheCrcMisses, " - << "FullUpdates, " - << "TerseUpdates, " - << "CacheMissRequests, " - << "CacheMissResponses, " - << "CacheUpdateDupes, " - << "CacheUpdateChanges, " - << "CacheUpdateAdds, " - << "CacheUpdateReplacements, " - << "UpdateFailures" + data_msg << "EventTime(ms), " + << "Processing Time(ms), " + << "Cache Hits, " + << "Cache Full Misses, " + << "Cache Crc Misses, " + << "Full Updates, " + << "Terse Updates, " + << "Cache Miss Requests, " + << "Cache Miss Responses, " + << "Cache Update Dupes, " + << "Cache Update Changes, " + << "Cache Update Adds, " + << "Cache Update Replacements, " + << "Update Failures, " + << "Cache Hits bps, " + << "Cache Full Misses bps, " + << "Cache Crc Misses bps, " + << "Full Updates bps, " + << "Terse Updates bps, " + << "Cache Miss Responses bps, " << "\n"; fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile ); } } -} - -void LLViewerStatsRecorder::beginObjectUpdateEvents(LLViewerRegion *regionp) -{ - initStatsRecorder(regionp); - mRegionp = regionp; - mProcessingTime = LLTimer::getTotalTime(); - clearStats(); + mProcessingStartTime = LLTimer::getTotalSeconds(); } void LLViewerStatsRecorder::clearStats() { mObjectCacheHitCount = 0; + mObjectCacheHitSize = 0; mObjectCacheMissFullCount = 0; + mObjectCacheMissFullSize = 0; mObjectCacheMissCrcCount = 0; + mObjectCacheMissCrcSize = 0; mObjectFullUpdates = 0; + mObjectFullUpdatesSize = 0; mObjectTerseUpdates = 0; + mObjectTerseUpdatesSize = 0; mObjectCacheMissRequests = 0; mObjectCacheMissResponses = 0; + mObjectCacheMissResponsesSize = 0; mObjectCacheUpdateDupes = 0; mObjectCacheUpdateChanges = 0; mObjectCacheUpdateAdds = 0; mObjectCacheUpdateReplacements = 0; mObjectUpdateFailures = 0; + mObjectUpdateFailuresSize = 0; } -void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type) +void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size) { mObjectUpdateFailures++; + mObjectUpdateFailuresSize += msg_size; } -void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type) +void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size) { if (LLViewerRegion::CACHE_MISS_TYPE_FULL == cache_miss_type) { mObjectCacheMissFullCount++; + mObjectCacheMissFullSize += msg_size; } else { mObjectCacheMissCrcCount++; + mObjectCacheMissCrcSize += msg_size; } } -void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp) +void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size) { switch (update_type) { case OUT_FULL: mObjectFullUpdates++; + mObjectFullUpdatesSize += msg_size; break; case OUT_TERSE_IMPROVED: mObjectTerseUpdates++; + mObjectTerseUpdatesSize += msg_size; break; case OUT_FULL_COMPRESSED: mObjectCacheMissResponses++; + mObjectCacheMissResponsesSize += msg_size; break; case OUT_FULL_CACHED: mObjectCacheHitCount++; + mObjectCacheHitSize += msg_size; break; default: llwarns << "Unknown update_type" << llendl; @@ -174,7 +177,7 @@ void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectU }; } -void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp) +void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size) { switch (update_result) { @@ -203,53 +206,72 @@ void LLViewerStatsRecorder::recordRequestCacheMissesEvent(S32 count) void LLViewerStatsRecorder::endObjectUpdateEvents() { - llinfos << "ILX: " - << mObjectCacheHitCount << " hits, " - << mObjectCacheMissFullCount << " full misses, " - << mObjectCacheMissCrcCount << " crc misses, " - << mObjectFullUpdates << " full updates, " - << mObjectTerseUpdates << " terse updates, " - << mObjectCacheMissRequests << " cache miss requests, " - << mObjectCacheMissResponses << " cache miss responses, " - << mObjectCacheUpdateDupes << " cache update dupes, " - << mObjectCacheUpdateChanges << " cache update changes, " - << mObjectCacheUpdateAdds << " cache update adds, " - << mObjectCacheUpdateReplacements << " cache update replacements, " - << mObjectUpdateFailures << " update failures" - << llendl; + mProcessingTotalTime += LLTimer::getTotalSeconds() - mProcessingStartTime; + takeSnapshot(); +} - S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; - if (mObjectCacheFile != NULL && - total_objects > 0) +void LLViewerStatsRecorder::takeSnapshot() +{ + F64 delta_time = LLTimer::getTotalSeconds() - mLastSnapshotTime; + if ( delta_time > mSnapshotInterval) { - std::ostringstream data_msg; - F32 processing32 = (F32) ((LLTimer::getTotalTime() - mProcessingTime) / 1000.0); + mLastSnapshotTime = LLTimer::getTotalSeconds(); + llinfos << "ILX: " + << mObjectCacheHitCount << " hits, " + << mObjectCacheMissFullCount << " full misses, " + << mObjectCacheMissCrcCount << " crc misses, " + << mObjectFullUpdates << " full updates, " + << mObjectTerseUpdates << " terse updates, " + << mObjectCacheMissRequests << " cache miss requests, " + << mObjectCacheMissResponses << " cache miss responses, " + << mObjectCacheUpdateDupes << " cache update dupes, " + << mObjectCacheUpdateChanges << " cache update changes, " + << mObjectCacheUpdateAdds << " cache update adds, " + << mObjectCacheUpdateReplacements << " cache update replacements, " + << mObjectUpdateFailures << " update failures" + << llendl; - data_msg << getTimeSinceStart() - << ", " << processing32 - << ", " << mObjectCacheHitCount - << ", " << mObjectCacheMissFullCount - << ", " << mObjectCacheMissCrcCount - << ", " << mObjectFullUpdates - << ", " << mObjectTerseUpdates - << ", " << mObjectCacheMissRequests - << ", " << mObjectCacheMissResponses - << ", " << mObjectCacheUpdateDupes - << ", " << mObjectCacheUpdateChanges - << ", " << mObjectCacheUpdateAdds - << ", " << mObjectCacheUpdateReplacements - << ", " << mObjectUpdateFailures - << "\n"; + S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; + if (mObjectCacheFile != NULL && + total_objects > 0) + { + std::ostringstream data_msg; - fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile ); - } + F32 processing32 = (F32) mProcessingTotalTime; + mProcessingTotalTime = 0.0; - clearStats(); + data_msg << getTimeSinceStart() + << ", " << processing32 + << ", " << mObjectCacheHitCount + << ", " << mObjectCacheMissFullCount + << ", " << mObjectCacheMissCrcCount + << ", " << mObjectFullUpdates + << ", " << mObjectTerseUpdates + << ", " << mObjectCacheMissRequests + << ", " << mObjectCacheMissResponses + << ", " << mObjectCacheUpdateDupes + << ", " << mObjectCacheUpdateChanges + << ", " << mObjectCacheUpdateAdds + << ", " << mObjectCacheUpdateReplacements + << ", " << mObjectUpdateFailures + << ", " << (mObjectCacheHitSize * 8 / delta_time) + << ", " << (mObjectCacheMissFullSize * 8 / delta_time) + << ", " << (mObjectCacheMissCrcSize * 8 / delta_time) + << ", " << (mObjectFullUpdatesSize * 8 / delta_time) + << ", " << (mObjectTerseUpdatesSize * 8 / delta_time) + << ", " << (mObjectCacheMissResponsesSize * 8 / delta_time) + << "\n"; + + fwrite(data_msg.str().c_str(), 1, data_msg.str().size(), mObjectCacheFile ); + } + + clearStats(); + } } F32 LLViewerStatsRecorder::getTimeSinceStart() { - return (F32) ((LLTimer::getTotalTime() - mStartTime) / 1000.0); + return (F32) (LLTimer::getTotalSeconds() - mStartTime); } #endif diff --git a/indra/newview/llviewerstatsrecorder.h b/indra/newview/llviewerstatsrecorder.h index 612ac380f7..09530b13eb 100644 --- a/indra/newview/llviewerstatsrecorder.h +++ b/indra/newview/llviewerstatsrecorder.h @@ -32,7 +32,7 @@ // for analysis. // This is normally 0. Set to 1 to enable viewer stats recording -#define LL_RECORD_VIEWER_STATS 0 +#define LL_RECORD_VIEWER_STATS 1 #if LL_RECORD_VIEWER_STATS @@ -41,52 +41,59 @@ #include "llviewerregion.h" class LLMutex; -class LLViewerRegion; class LLViewerObject; -class LLViewerStatsRecorder +class LLViewerStatsRecorder : public LLSingleton { public: + LOG_CLASS(LLViewerStatsRecorder); LLViewerStatsRecorder(); ~LLViewerStatsRecorder(); - static void initClass(); - static void cleanupClass(); - static LLViewerStatsRecorder* instance() {return sInstance; } + void beginObjectUpdateEvents(F32 interval); - void initStatsRecorder(LLViewerRegion *regionp); - - void beginObjectUpdateEvents(LLViewerRegion *regionp); - void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type); - void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type); - void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp); - void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp); + void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size); + void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size); + void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size); + void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size); void recordRequestCacheMissesEvent(S32 count); + void endObjectUpdateEvents(); F32 getTimeSinceStart(); private: + void takeSnapshot(); + static LLViewerStatsRecorder* sInstance; LLFILE * mObjectCacheFile; // File to write data into LLFrameTimer mTimer; - LLViewerRegion* mRegionp; F64 mStartTime; - F64 mProcessingTime; + F64 mProcessingStartTime; + F64 mProcessingTotalTime; + F64 mSnapshotInterval; + F64 mLastSnapshotTime; S32 mObjectCacheHitCount; + S32 mObjectCacheHitSize; S32 mObjectCacheMissFullCount; + S32 mObjectCacheMissFullSize; S32 mObjectCacheMissCrcCount; + S32 mObjectCacheMissCrcSize; S32 mObjectFullUpdates; + S32 mObjectFullUpdatesSize; S32 mObjectTerseUpdates; + S32 mObjectTerseUpdatesSize; S32 mObjectCacheMissRequests; S32 mObjectCacheMissResponses; + S32 mObjectCacheMissResponsesSize; S32 mObjectCacheUpdateDupes; S32 mObjectCacheUpdateChanges; S32 mObjectCacheUpdateAdds; S32 mObjectCacheUpdateReplacements; S32 mObjectUpdateFailures; + S32 mObjectUpdateFailuresSize; void clearStats(); -- cgit v1.3 From df7d6c90758eef95f2c8c004611add495b8a5eee Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Wed, 18 Jul 2012 12:37:52 -0700 Subject: SH-3275 WIP Run viewer metrics for object update messages continued clean up of llstats stuff --- indra/newview/llappviewer.cpp | 6 +- indra/newview/llstartup.cpp | 2 +- indra/newview/llsurface.cpp | 2 - indra/newview/llsurface.h | 3 - indra/newview/lltexturefetch.cpp | 8 +- indra/newview/llviewermenu.cpp | 17 --- indra/newview/llviewerstats.cpp | 194 ++++------------------------------ indra/newview/llviewerstats.h | 157 +++++++++++++-------------- indra/newview/llviewertexturelist.cpp | 8 -- indra/newview/llviewerwindow.cpp | 20 ++-- indra/newview/llvlcomposition.cpp | 2 - 11 files changed, 108 insertions(+), 311 deletions(-) (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c02bc882ea..161cc418dd 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -785,9 +785,6 @@ bool LLAppViewer::init() ////////////////////////////////////////////////////////////////////////////// // *FIX: The following code isn't grouped into functions yet. - // Statistics / debug timer initialization - init_statistics(); - // // Various introspection concerning the libs we're using - particularly // the libs involved in getting to a full login screen. @@ -4208,7 +4205,6 @@ void LLAppViewer::idle() // of SEND_STATS_PERIOD so that the initial stats report will // be sent immediately. static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD); - reset_statistics(); // Update session stats every large chunk of time // *FIX: (???) SAMANTHA @@ -4268,7 +4264,7 @@ void LLAppViewer::idle() idle_afk_check(); // Update statistics for this frame - update_statistics(gFrameCount); + update_statistics(); } //////////////////////////////////////// diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6b0fc26db7..77d0932fc0 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1394,7 +1394,7 @@ bool idle_startup() display_startup(); //reset statistics - LLViewerStats::getInstance()->resetStats(); + LLViewerStats::instance().resetStats(); display_startup(); // diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp index 66df7dae3e..3bd2d4cabc 100644 --- a/indra/newview/llsurface.cpp +++ b/indra/newview/llsurface.cpp @@ -61,8 +61,6 @@ LLColor4U MAX_WATER_COLOR(0, 48, 96, 240); S32 LLSurface::sTextureSize = 256; -S32 LLSurface::sTexelsUpdated = 0; -F32 LLSurface::sTextureUpdateTime = 0.f; // ---------------- LLSurface:: Public Members --------------- diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index a4ef4fe2de..eb120f142d 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -168,9 +168,6 @@ public: F32 mDetailTextureScale; // Number of times to repeat detail texture across this surface - static F32 sTextureUpdateTime; - static S32 sTexelsUpdated; - protected: void createSTexture(); void createWaterTexture(); diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 143db79eb5..001060f4de 100755 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -3242,7 +3242,7 @@ void LLTextureFetchDebugger::startDebug() } //collect statistics - mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mTotalFetchingTime = gTextureTimer.getElapsedTimeF32() - mTotalFetchingTime; std::set fetched_textures; S32 size = mFetchingHistory.size(); @@ -3324,7 +3324,7 @@ void LLTextureFetchDebugger::stopDebug() //unlock the fetcher mFetcher->lockFetcher(false); mFreezeHistory = FALSE; - mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset + mTotalFetchingTime = gTextureTimer.getElapsedTimeF32(); //reset } //called in the main thread and when the fetching queue is empty @@ -3637,7 +3637,7 @@ bool LLTextureFetchDebugger::update() case REFETCH_VIS_CACHE: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisCacheTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisCacheTime = gTextureTimer.getElapsedTimeF32() - mTotalFetchingTime; mState = IDLE; mFetcher->lockFetcher(true); } @@ -3645,7 +3645,7 @@ bool LLTextureFetchDebugger::update() case REFETCH_VIS_HTTP: if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - mRefetchVisHTTPTime = gDebugTimers[0].getElapsedTimeF32() - mTotalFetchingTime; + mRefetchVisHTTPTime = gTextureTimer.getElapsedTimeF32() - mTotalFetchingTime; mState = IDLE; mFetcher->lockFetcher(true); } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 34e916fec0..75b4815ba7 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1314,22 +1314,6 @@ class LLAdvancedPrintAgentInfo : public view_listener_t } }; - - -//////////////////////////////// -// PRINT TEXTURE MEMORY STATS // -//////////////////////////////// - - -class LLAdvancedPrintTextureMemoryStats : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - output_statistics(NULL); - return true; - } -}; - ////////////////// // DEBUG CLICKS // ////////////////// @@ -8280,7 +8264,6 @@ void initialize_menus() commit.add("Advanced.DumpFocusHolder", boost::bind(&handle_dump_focus) ); view_listener_t::addMenu(new LLAdvancedPrintSelectedObjectInfo(), "Advanced.PrintSelectedObjectInfo"); view_listener_t::addMenu(new LLAdvancedPrintAgentInfo(), "Advanced.PrintAgentInfo"); - view_listener_t::addMenu(new LLAdvancedPrintTextureMemoryStats(), "Advanced.PrintTextureMemoryStats"); view_listener_t::addMenu(new LLAdvancedToggleDebugClicks(), "Advanced.ToggleDebugClicks"); view_listener_t::addMenu(new LLAdvancedCheckDebugClicks(), "Advanced.CheckDebugClicks"); view_listener_t::addMenu(new LLAdvancedCheckDebugViews(), "Advanced.CheckDebugViews"); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index c1ccfe3faa..fe78b1232c 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -286,19 +286,19 @@ LLViewerStats::~LLViewerStats() void LLViewerStats::resetStats() { - LLViewerStats::getInstance()->mKBitStat.reset(); - LLViewerStats::getInstance()->mLayersKBitStat.reset(); - LLViewerStats::getInstance()->mObjectKBitStat.reset(); - LLViewerStats::getInstance()->mTextureKBitStat.reset(); - LLViewerStats::getInstance()->mVFSPendingOperations.reset(); - LLViewerStats::getInstance()->mAssetKBitStat.reset(); - LLViewerStats::getInstance()->mPacketsInStat.reset(); - LLViewerStats::getInstance()->mPacketsLostStat.reset(); - LLViewerStats::getInstance()->mPacketsOutStat.reset(); - LLViewerStats::getInstance()->mFPSStat.reset(); - LLViewerStats::getInstance()->mTexturePacketsStat.reset(); - - LLViewerStats::getInstance()->mAgentPositionSnaps.reset(); + LLViewerStats& stats = LLViewerStats::instance(); + stats.mKBitStat.reset(); + stats.mLayersKBitStat.reset(); + stats.mObjectKBitStat.reset(); + stats.mTextureKBitStat.reset(); + stats.mVFSPendingOperations.reset(); + stats.mAssetKBitStat.reset(); + stats.mPacketsInStat.reset(); + stats.mPacketsLostStat.reset(); + stats.mPacketsOutStat.reset(); + stats.mFPSStat.reset(); + stats.mTexturePacketsStat.reset(); + stats.mAgentPositionSnaps.reset(); } @@ -401,140 +401,6 @@ void LLViewerStats::addToMessage(LLSD &body) const // *NOTE:Mani The following methods used to exist in viewer.cpp // Moving them here, but not merging them into LLViewerStats yet. -void reset_statistics() -{ - if (LLSurface::sTextureUpdateTime) - { - LLSurface::sTexelsUpdated = 0; - LLSurface::sTextureUpdateTime = 0.f; - } -} - - -void output_statistics(void*) -{ - llinfos << "Number of orphans: " << gObjectList.getOrphanCount() << llendl; - llinfos << "Number of dead objects: " << gObjectList.mNumDeadObjects << llendl; - llinfos << "Num images: " << gTextureList.getNumImages() << llendl; - llinfos << "Texture usage: " << LLImageGL::sGlobalTextureMemoryInBytes << llendl; - llinfos << "Texture working set: " << LLImageGL::sBoundTextureMemoryInBytes << llendl; - llinfos << "Raw usage: " << LLImageRaw::sGlobalRawMemory << llendl; - llinfos << "Formatted usage: " << LLImageFormatted::sGlobalFormattedMemory << llendl; - llinfos << "Zombie Viewer Objects: " << LLViewerObject::getNumZombieObjects() << llendl; - llinfos << "Number of lights: " << gPipeline.getLightCount() << llendl; - - llinfos << "Memory Usage:" << llendl; - llinfos << "--------------------------------" << llendl; - llinfos << "Pipeline:" << llendl; - llinfos << llendl; - -#if LL_SMARTHEAP - llinfos << "--------------------------------" << llendl; - { - llinfos << "sizeof(LLVOVolume) = " << sizeof(LLVOVolume) << llendl; - - U32 total_pool_size = 0; - U32 total_used_size = 0; - MEM_POOL_INFO pool_info; - MEM_POOL_STATUS pool_status; - U32 pool_num = 0; - for(pool_status = MemPoolFirst( &pool_info, 1 ); - pool_status != MEM_POOL_END; - pool_status = MemPoolNext( &pool_info, 1 ) ) - { - llinfos << "Pool #" << pool_num << llendl; - if( MEM_POOL_OK != pool_status ) - { - llwarns << "Pool not ok" << llendl; - continue; - } - - llinfos << "Pool blockSizeFS " << pool_info.blockSizeFS - << " pageSize " << pool_info.pageSize - << llendl; - - U32 pool_count = MemPoolCount(pool_info.pool); - llinfos << "Blocks " << pool_count << llendl; - - U32 pool_size = MemPoolSize( pool_info.pool ); - if( pool_size == MEM_ERROR_RET ) - { - llinfos << "MemPoolSize() failed (" << pool_num << ")" << llendl; - } - else - { - llinfos << "MemPool Size " << pool_size / 1024 << "K" << llendl; - } - - total_pool_size += pool_size; - - if( !MemPoolLock( pool_info.pool ) ) - { - llinfos << "MemPoolLock failed (" << pool_num << ") " << llendl; - continue; - } - - U32 used_size = 0; - MEM_POOL_ENTRY entry; - entry.entry = NULL; - while( MemPoolWalk( pool_info.pool, &entry ) == MEM_POOL_OK ) - { - if( entry.isInUse ) - { - used_size += entry.size; - } - } - - MemPoolUnlock( pool_info.pool ); - - llinfos << "MemPool Used " << used_size/1024 << "K" << llendl; - total_used_size += used_size; - pool_num++; - } - - llinfos << "Total Pool Size " << total_pool_size/1024 << "K" << llendl; - llinfos << "Total Used Size " << total_used_size/1024 << "K" << llendl; - - } -#endif - - llinfos << "--------------------------------" << llendl; - llinfos << "Avatar Memory (partly overlaps with above stats):" << llendl; - LLTexLayerStaticImageList::getInstance()->dumpByteCount(); - LLVOAvatarSelf::dumpScratchTextureByteCount(); - LLTexLayerSetBuffer::dumpTotalByteCount(); - LLVOAvatarSelf::dumpTotalLocalTextureByteCount(); - LLTexLayerParamAlpha::dumpCacheByteCount(); - LLVOAvatar::dumpBakedStatus(); - - llinfos << llendl; - - llinfos << "Object counts:" << llendl; - S32 i; - S32 obj_counts[256]; -// S32 app_angles[256]; - for (i = 0; i < 256; i++) - { - obj_counts[i] = 0; - } - for (i = 0; i < gObjectList.getNumObjects(); i++) - { - LLViewerObject *objectp = gObjectList.getObject(i); - if (objectp) - { - obj_counts[objectp->getPCode()]++; - } - } - for (i = 0; i < 256; i++) - { - if (obj_counts[i]) - { - llinfos << LLPrimitive::pCodeToString(i) << ":" << obj_counts[i] << llendl; - } - } -} - - U32 gTotalLandIn = 0, gTotalLandOut = 0; U32 gTotalWaterIn = 0, gTotalWaterOut = 0; @@ -552,16 +418,9 @@ U32 gTotalTextureBytesPerBoostLevel[LLViewerTexture::MAX_GL_IMAGE_CATEGORY] extern U32 gVisCompared; extern U32 gVisTested; -std::map gDebugTimers; -std::map gDebugTimerLabel; +extern LLFrameTimer gTextureTimer; -void init_statistics() -{ - // Label debug timers - gDebugTimerLabel[0] = "Texture"; -} - -void update_statistics(U32 frame_count) +void update_statistics() { gTotalWorldBytes += gVLManager.getTotalBytes(); gTotalObjectBytes += gObjectBits / 8; @@ -588,11 +447,7 @@ void update_statistics(U32 frame_count) stats.setStat(LLViewerStats::ST_LIGHTING_DETAIL, (F64)gPipeline.getLightingDetail()); stats.setStat(LLViewerStats::ST_DRAW_DIST, (F64)gSavedSettings.getF32("RenderFarClip")); stats.setStat(LLViewerStats::ST_CHAT_BUBBLES, (F64)gSavedSettings.getBOOL("UseChatBubbles")); -#if 0 // 1.9.2 - stats.setStat(LLViewerStats::ST_SHADER_OBJECTS, (F64)gSavedSettings.getS32("VertexShaderLevelObject")); - stats.setStat(LLViewerStats::ST_SHADER_AVATAR, (F64)gSavedSettings.getBOOL("VertexShaderLevelAvatar")); - stats.setStat(LLViewerStats::ST_SHADER_ENVIRONMENT, (F64)gSavedSettings.getBOOL("VertexShaderLevelEnvironment")); -#endif + stats.setStat(LLViewerStats::ST_FRAME_SECS, gDebugView->mFastTimerView->getTime("Frame")); F64 idle_secs = gDebugView->mFastTimerView->getTime("Idle"); F64 network_secs = gDebugView->mFastTimerView->getTime("Network"); @@ -624,11 +479,11 @@ void update_statistics(U32 frame_count) if (LLAppViewer::getTextureFetch()->getNumRequests() == 0) { - gDebugTimers[0].pause(); + gTextureTimer.pause(); } else { - gDebugTimers[0].unpause(); + gTextureTimer.unpause(); } { @@ -664,7 +519,6 @@ void update_statistics(U32 frame_count) texture_stats_timer.reset(); } } - } class ViewerStatsResponder : public LLHTTPClient::Responder @@ -824,10 +678,7 @@ void send_stats() S32 window_height = gViewerWindow->getWindowHeightRaw(); S32 window_size = (window_width * window_height) / 1024; misc["string_1"] = llformat("%d", window_size); - if (gDebugTimers.find(0) != gDebugTimers.end() && gFrameTimeSeconds > 0) - { - misc["string_2"] = llformat("Texture Time: %.2f, Total Time: %.2f", gDebugTimers[0].getElapsedTimeF32(), gFrameTimeSeconds); - } + misc["string_2"] = llformat("Texture Time: %.2f, Total Time: %.2f", gTextureTimer.getElapsedTimeF32(), gFrameTimeSeconds); // misc["int_1"] = LLSD::Integer(gSavedSettings.getU32("RenderQualityPerformance")); // Steve: 1.21 // misc["int_2"] = LLSD::Integer(gFrameStalls); // Steve: 1.21 @@ -918,13 +769,6 @@ LLSD LLViewerStats::PhaseMap::dumpPhases() const std::string& phase_name = iter->first; result[phase_name]["completed"] = !(iter->second.getStarted()); result[phase_name]["elapsed"] = iter->second.getElapsedTimeF32(); -#if 0 // global stats for each phase seem like overkill here - phase_stats_t::iterator stats_iter = sPhaseStats.find(phase_name); - if (stats_iter != sPhaseStats.end()) - { - result[phase_name]["stats"] = stats_iter->second.getData(); - } -#endif } return result; } diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 9a2a33fa7b..0402b82154 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -33,82 +33,82 @@ class LLViewerStats : public LLSingleton { public: - LLStat mKBitStat; - LLStat mLayersKBitStat; - LLStat mObjectKBitStat; - LLStat mAssetKBitStat; - LLStat mTextureKBitStat; - LLStat mVFSPendingOperations; - LLStat mObjectsDrawnStat; - LLStat mObjectsCulledStat; - LLStat mObjectsTestedStat; - LLStat mObjectsComparedStat; - LLStat mObjectsOccludedStat; - LLStat mFPSStat; - LLStat mPacketsInStat; - LLStat mPacketsLostStat; - LLStat mPacketsOutStat; - LLStat mPacketsLostPercentStat; - LLStat mTexturePacketsStat; - LLStat mActualInKBitStat; // From the packet ring (when faking a bad connection) - LLStat mActualOutKBitStat; // From the packet ring (when faking a bad connection) - LLStat mTrianglesDrawnStat; + LLStat mKBitStat, + mLayersKBitStat, + mObjectKBitStat, + mAssetKBitStat, + mTextureKBitStat, + mVFSPendingOperations, + mObjectsDrawnStat, + mObjectsCulledStat, + mObjectsTestedStat, + mObjectsComparedStat, + mObjectsOccludedStat, + mFPSStat, + mPacketsInStat, + mPacketsLostStat, + mPacketsOutStat, + mPacketsLostPercentStat, + mTexturePacketsStat, + mActualInKBitStat, // From the packet ring (when faking a bad connection) + mActualOutKBitStat, // From the packet ring (when faking a bad connection) + mTrianglesDrawnStat; // Simulator stats - LLStat mSimTimeDilation; - - LLStat mSimFPS; - LLStat mSimPhysicsFPS; - LLStat mSimAgentUPS; - LLStat mSimScriptEPS; - - LLStat mSimFrameMsec; - LLStat mSimNetMsec; - LLStat mSimSimOtherMsec; - LLStat mSimSimPhysicsMsec; - - LLStat mSimSimPhysicsStepMsec; - LLStat mSimSimPhysicsShapeUpdateMsec; - LLStat mSimSimPhysicsOtherMsec; - - LLStat mSimAgentMsec; - LLStat mSimImagesMsec; - LLStat mSimScriptMsec; - LLStat mSimSpareMsec; - LLStat mSimSleepMsec; - LLStat mSimPumpIOMsec; - - LLStat mSimMainAgents; - LLStat mSimChildAgents; - LLStat mSimObjects; - LLStat mSimActiveObjects; - LLStat mSimActiveScripts; - - LLStat mSimInPPS; - LLStat mSimOutPPS; - LLStat mSimPendingDownloads; - LLStat mSimPendingUploads; - LLStat mSimPendingLocalUploads; - LLStat mSimTotalUnackedBytes; - - LLStat mPhysicsPinnedTasks; - LLStat mPhysicsLODTasks; - LLStat mPhysicsMemoryAllocated; - - LLStat mSimPingStat; - - LLStat mNumImagesStat; - LLStat mNumRawImagesStat; - LLStat mGLTexMemStat; - LLStat mGLBoundMemStat; - LLStat mRawMemStat; - LLStat mFormattedMemStat; - - LLStat mNumObjectsStat; - LLStat mNumActiveObjectsStat; - LLStat mNumNewObjectsStat; - LLStat mNumSizeCulledStat; - LLStat mNumVisCulledStat; + LLStat mSimTimeDilation, + + mSimFPS, + mSimPhysicsFPS, + mSimAgentUPS, + mSimScriptEPS, + + mSimFrameMsec, + mSimNetMsec, + mSimSimOtherMsec, + mSimSimPhysicsMsec, + + mSimSimPhysicsStepMsec, + mSimSimPhysicsShapeUpdateMsec, + mSimSimPhysicsOtherMsec, + + mSimAgentMsec, + mSimImagesMsec, + mSimScriptMsec, + mSimSpareMsec, + mSimSleepMsec, + mSimPumpIOMsec, + + mSimMainAgents, + mSimChildAgents, + mSimObjects, + mSimActiveObjects, + mSimActiveScripts, + + mSimInPPS, + mSimOutPPS, + mSimPendingDownloads, + mSimPendingUploads, + mSimPendingLocalUploads, + mSimTotalUnackedBytes, + + mPhysicsPinnedTasks, + mPhysicsLODTasks, + mPhysicsMemoryAllocated, + + mSimPingStat, + + mNumImagesStat, + mNumRawImagesStat, + mGLTexMemStat, + mGLBoundMemStat, + mRawMemStat, + mFormattedMemStat, + + mNumObjectsStat, + mNumActiveObjectsStat, + mNumNewObjectsStat, + mNumSizeCulledStat, + mNumVisCulledStat; void resetStats(); public: @@ -177,7 +177,6 @@ public: ST_COUNT = 58 }; - LLViewerStats(); ~LLViewerStats(); @@ -304,14 +303,10 @@ private: static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. -void init_statistics(); -void reset_statistics(); -void output_statistics(void*); -void update_statistics(U32 frame_count); +void update_statistics(); void send_stats(); -extern std::map gDebugTimers; -extern std::map gDebugTimerLabel; +extern LLFrameTimer gTextureTimer; extern U32 gTotalTextureBytes; extern U32 gTotalObjectBytes; extern U32 gTotalTextureBytesPerBoostLevel[] ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 528e0080b7..9e1e92f8e8 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -906,14 +906,6 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) } min_count--; } - //if (fetch_count == 0) - //{ - // gDebugTimers[0].pause(); - //} - //else - //{ - // gDebugTimers[0].unpause(); - //} return image_op_timer.getElapsedTimeF32(); } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 39e330ad66..9e9b41d3c8 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -333,19 +333,13 @@ public: if (gSavedSettings.getBOOL("DebugShowTime")) { const U32 y_inc2 = 15; - for (std::map::reverse_iterator iter = gDebugTimers.rbegin(); - iter != gDebugTimers.rend(); ++iter) - { - S32 idx = iter->first; - LLFrameTimer& timer = iter->second; - F32 time = timer.getElapsedTimeF32(); - S32 hours = (S32)(time / (60*60)); - S32 mins = (S32)((time - hours*(60*60)) / 60); - S32 secs = (S32)((time - hours*(60*60) - mins*60)); - std::string label = gDebugTimerLabel[idx]; - if (label.empty()) label = llformat("Debug: %d", idx); - addText(xpos, ypos, llformat(" %s: %d:%02d:%02d", label.c_str(), hours,mins,secs)); ypos += y_inc2; - } + LLFrameTimer& timer = gTextureTimer; + F32 time = timer.getElapsedTimeF32(); + S32 hours = (S32)(time / (60*60)); + S32 mins = (S32)((time - hours*(60*60)) / 60); + S32 secs = (S32)((time - hours*(60*60) - mins*60)); + addText(xpos, ypos, llformat(" "Texture": %d:%02d:%02d", hours,mins,secs)); ypos += y_inc2; + F32 time = gFrameTimeSeconds; S32 hours = (S32)(time / (60*60)); diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index ec932501e5..abb5153480 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -457,8 +457,6 @@ BOOL LLVLComposition::generateTexture(const F32 x, const F32 y, texturep->createGLTexture(0, raw); } texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin); - LLSurface::sTextureUpdateTime += gen_timer.getElapsedTimeF32(); - LLSurface::sTexelsUpdated += (tex_x_end - tex_x_begin) * (tex_y_end - tex_y_begin); for (S32 i = 0; i < 4; i++) { -- cgit v1.3 From 0ee0a5eff41a3763b1fc3fc45a36f87fc6eedac5 Mon Sep 17 00:00:00 2001 From: Paul ProductEngine Date: Fri, 27 Jul 2012 22:25:17 +0300 Subject: CHUI-151 FIXED (Implement conversation log) A brief explanation of what have been implemented. More information can be found in comments. 1. Created conversation history viewer (llfloaterconversationpreview) 2. Created LLConversation and LLConversationLog classes which represent and hold data of conversations (llconversationlog) 3. Created LLConversationLogList and LLConversationLogListItem which are the visual representation of LLConversationLog and LLConversation respectively 4. Created LLFloaterConversationLog - which holds and displays LLConversationLogList --- indra/newview/CMakeLists.txt | 10 + indra/newview/app_settings/settings.xml | 22 ++ indra/newview/llappviewer.cpp | 4 + indra/newview/llconversationlog.cpp | 336 ++++++++++++++++ indra/newview/llconversationlog.h | 164 ++++++++ indra/newview/llconversationloglist.cpp | 422 +++++++++++++++++++++ indra/newview/llconversationloglist.h | 143 +++++++ indra/newview/llconversationloglistitem.cpp | 157 ++++++++ indra/newview/llconversationloglistitem.h | 77 ++++ indra/newview/llfloaterconversationlog.cpp | 127 +++++++ indra/newview/llfloaterconversationlog.h | 61 +++ indra/newview/llfloaterconversationpreview.cpp | 112 ++++++ indra/newview/llfloaterconversationpreview.h | 51 +++ indra/newview/llimfloater.cpp | 12 + indra/newview/llimfloater.h | 6 + indra/newview/llimview.cpp | 17 +- indra/newview/llimview.h | 10 +- indra/newview/llstartup.cpp | 3 + indra/newview/llviewerfloaterreg.cpp | 4 + indra/newview/llviewermessage.cpp | 5 +- indra/newview/llvoicevivox.cpp | 1 + .../default/xui/en/floater_conversation_log.xml | 84 ++++ .../xui/en/floater_conversation_preview.xml | 53 +++ .../skins/default/xui/en/floater_im_container.xml | 1 + .../default/xui/en/menu_conversation_log_gear.xml | 134 +++++++ .../default/xui/en/menu_conversation_log_view.xml | 37 ++ .../skins/default/xui/en/menu_participant_view.xml | 16 + .../xui/en/panel_conversation_log_list_item.xml | 108 ++++++ 28 files changed, 2167 insertions(+), 10 deletions(-) create mode 100644 indra/newview/llconversationlog.cpp create mode 100644 indra/newview/llconversationlog.h create mode 100644 indra/newview/llconversationloglist.cpp create mode 100644 indra/newview/llconversationloglist.h create mode 100644 indra/newview/llconversationloglistitem.cpp create mode 100644 indra/newview/llconversationloglistitem.h create mode 100644 indra/newview/llfloaterconversationlog.cpp create mode 100644 indra/newview/llfloaterconversationlog.h create mode 100644 indra/newview/llfloaterconversationpreview.cpp create mode 100644 indra/newview/llfloaterconversationpreview.h create mode 100644 indra/newview/skins/default/xui/en/floater_conversation_log.xml create mode 100644 indra/newview/skins/default/xui/en/floater_conversation_preview.xml create mode 100644 indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml create mode 100644 indra/newview/skins/default/xui/en/menu_conversation_log_view.xml create mode 100644 indra/newview/skins/default/xui/en/menu_participant_view.xml create mode 100644 indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml (limited to 'indra/newview/llappviewer.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b31b99f47c..3fe1aec5ff 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -130,6 +130,9 @@ set(viewer_SOURCE_FILES llcommandlineparser.cpp llcompilequeue.cpp llconfirmationmanager.cpp + llconversationlog.cpp + llconversationloglist.cpp + llconversationloglistitem.cpp llcurrencyuimanager.cpp llcylinder.cpp lldateutil.cpp @@ -185,6 +188,8 @@ set(viewer_SOURCE_FILES llfloaterbuyland.cpp llfloatercamera.cpp llfloatercolorpicker.cpp + llfloaterconversationlog.cpp + llfloaterconversationpreview.cpp llfloaterdeleteenvpreset.cpp llfloaterdestinations.cpp llfloaterdisplayname.cpp @@ -687,6 +692,9 @@ set(viewer_HEADER_FILES llcommandlineparser.h llcompilequeue.h llconfirmationmanager.h + llconversationlog.h + llconversationloglist.h + llconversationloglistitem.h llcurrencyuimanager.h llcylinder.h lldateutil.h @@ -742,6 +750,8 @@ set(viewer_HEADER_FILES llfloaterbuyland.h llfloatercamera.h llfloatercolorpicker.h + llfloaterconversationlog.h + llfloaterconversationpreview.h llfloaterdeleteenvpreset.h llfloaterdestinations.h llfloaterdisplayname.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index da3ff2d1ee..7a5abba971 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10004,6 +10004,28 @@ Value 2 + CallLogSortOrder + + Comment + Specifies sort order for Call Log (0 = by name, 1 = by date) + Persist + 1 + Type + U32 + Value + 2 + + SortFriendsFirst + + Comment + Specifies whether friends will be sorted first in Call Log + Persist + 1 + Type + Boolean + Value + 1 + ShowPGSearchAll Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1174d108d2..fe3a1ebf65 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -59,6 +59,7 @@ #include "llares.h" #include "llcurl.h" #include "llcalc.h" +#include "llconversationlog.h" #include "lltexturestats.h" #include "lltexturestats.h" #include "llviewerwindow.h" @@ -1757,6 +1758,9 @@ bool LLAppViewer::cleanup() // save mute list. gMuteList used to also be deleted here too. LLMuteList::getInstance()->cache(gAgent.getID()); + //save call log list + LLConversationLog::instance().cache(); + if (mPurgeOnExit) { llinfos << "Purging all cache files on exit" << llendflush; diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp new file mode 100644 index 0000000000..df9350407d --- /dev/null +++ b/indra/newview/llconversationlog.cpp @@ -0,0 +1,336 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llagent.h" +#include "llconversationlog.h" +#include "lltrans.h" + +struct Conversation_params +{ + Conversation_params(time_t time) + : mTime(time), + mTimestamp(LLConversation::createTimestamp(time)) + {} + + time_t mTime; + std::string mTimestamp; + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mIsVoice; + bool mHasOfflineIMs; +}; + +/************************************************************************/ +/* LLConversation implementation */ +/************************************************************************/ + +LLConversation::LLConversation(const Conversation_params& params) +: mTime(params.mTime), + mTimestamp(params.mTimestamp), + mConversationType(params.mConversationType), + mConversationName(params.mConversationName), + mHistoryFileName(params.mHistoryFileName), + mSessionID(params.mSessionID), + mParticipantID(params.mParticipantID), + mIsVoice(params.mIsVoice), + mHasOfflineIMs(params.mHasOfflineIMs) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLIMModel::LLIMSession& session) +: mTime(time_corrected()), + mTimestamp(createTimestamp(mTime)), + mConversationType(session.mSessionType), + mConversationName(session.mName), + mHistoryFileName(session.mHistoryFileName), + mSessionID(session.mSessionID), + mParticipantID(session.mOtherParticipantID), + mIsVoice(session.mStartedAsIMCall), + mHasOfflineIMs(session.mHasOfflineMessage) +{ + setListenIMFloaterOpened(); +} + +LLConversation::LLConversation(const LLConversation& conversation) +{ + mTime = conversation.getTime(); + mTimestamp = conversation.getTimestamp(); + mConversationType = conversation.getConversationType(); + mConversationName = conversation.getConversationName(); + mHistoryFileName = conversation.getHistoryFileName(); + mSessionID = conversation.getSessionID(); + mParticipantID = conversation.getParticipantID(); + mIsVoice = conversation.isVoice(); + mHasOfflineIMs = conversation.hasOfflineMessages(); + + setListenIMFloaterOpened(); +} + +LLConversation::~LLConversation() +{ + mIMFloaterShowedConnection.disconnect(); +} + +void LLConversation::onIMFloaterShown(const LLUUID& session_id) +{ + if (mSessionID == session_id) + { + mHasOfflineIMs = false; + } +} + +// static +const std::string LLConversation::createTimestamp(const time_t& utc_time) +{ + std::string timeStr; + LLSD substitution; + substitution["datetime"] = (S32) utc_time; + + timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" + +LLTrans::getString ("TimeDay")+"]/[" + +LLTrans::getString ("TimeYear")+"] [" + +LLTrans::getString ("TimeHour")+"]:[" + +LLTrans::getString ("TimeMin")+"]"; + + + LLStringUtil::format (timeStr, substitution); + return timeStr; +} + +void LLConversation::setListenIMFloaterOpened() +{ + LLIMFloater* floater = LLIMFloater::findInstance(mSessionID); + + bool has_offline_ims = !mIsVoice && mHasOfflineIMs; + bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + // we don't need to listen for im floater with this conversation is opened + // if floater is already opened or this conversation doesn't have unread offline messages + if (has_offline_ims && !ims_are_read) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1)); + } +} +/************************************************************************/ +/* LLConversationLog implementation */ +/************************************************************************/ + +LLConversationLog::LLConversationLog() +{ + loadFromFile(getFileName()); + + LLIMMgr::instance().addSessionObserver(this); + LLAvatarTracker::instance().addObserver(this); +} +void LLConversationLog::logConversation(const LLConversation& conversation) +{ + mConversations.push_back(conversation); + notifyObservers(); +} + +void LLConversationLog::removeConversation(const LLConversation& conversation) +{ + conversations_vec_t::iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == conversation.getSessionID() && conv_it->getTime() == conversation.getTime()) + { + mConversations.erase(conv_it); + notifyObservers(); + return; + } + } +} + +const LLConversation* LLConversationLog::getConversation(const LLUUID& session_id) +{ + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for(; conv_it != mConversations.end(); ++conv_it) + { + if (conv_it->getSessionID() == session_id) + { + return &*conv_it; + } + } + + return NULL; +} + +void LLConversationLog::addObserver(LLConversationLogObserver* observer) +{ + mObservers.insert(observer); +} + +void LLConversationLog::removeObserver(LLConversationLogObserver* observer) +{ + mObservers.erase(observer); +} + +void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + if (session) + { + LLConversation conversation(*session); + LLConversationLog::instance().logConversation(conversation); + } +} + +// LLFriendObserver +void LLConversationLog::changed(U32 mask) +{ + if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE)) + { + notifyObservers(); + } +} + +void LLConversationLog::cache() +{ + saveToFile(getFileName()); +} + +std::string LLConversationLog::getFileName() +{ + std::string agent_id_string; + gAgent.getID().toString(agent_id_string); + + return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".call_log"; +} + +bool LLConversationLog::saveToFile(const std::string& filename) +{ + if(!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "wb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + std::string participant_id; + std::string conversation_id; + + conversations_vec_t::const_iterator conv_it = mConversations.begin(); + for (; conv_it != mConversations.end(); ++conv_it) + { + conv_it->getSessionID().toString(conversation_id); + conv_it->getParticipantID().toString(participant_id); + + // examples of two file entries + // [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe| + // [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a| + + fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n", + (S32)conv_it->getTime(), + (S32)conv_it->getConversationType(), + (S32)conv_it->isVoice(), + (S32)conv_it->hasOfflineMessages(), + conv_it->getConversationName().c_str(), + participant_id.c_str(), + conversation_id.c_str(), + conv_it->getHistoryFileName().c_str()); + } + fclose(fp); + return true; +} +bool LLConversationLog::loadFromFile(const std::string& filename) +{ + if(!filename.size()) + { + llwarns << "Call log list filename is empty!" << llendl; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (!fp) + { + llwarns << "Couldn't open call log list" << filename << llendl; + return false; + } + + char buffer[MAX_STRING]; + char conv_name_buffer[MAX_STRING]; + char part_id_buffer[MAX_STRING]; + char conv_id_buffer[MAX_STRING]; + char history_file_name[MAX_STRING]; + int is_voice; + int has_offline_ims; + int stype; + S32 time; + + while (!feof(fp) && fgets(buffer, MAX_STRING, fp)) + { + conv_name_buffer[0] = '\0'; + part_id_buffer[0] = '\0'; + conv_id_buffer[0] = '\0'; + + sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|", + &time, + &stype, + &is_voice, + &has_offline_ims, + conv_name_buffer, + part_id_buffer, + conv_id_buffer, + history_file_name); + + Conversation_params params(time); + params.mConversationType = (SessionType)stype; + params.mIsVoice = is_voice; + params.mHasOfflineIMs = has_offline_ims; + params.mConversationName = std::string(conv_name_buffer); + params.mParticipantID = LLUUID(part_id_buffer); + params.mSessionID = LLUUID(conv_id_buffer); + params.mHistoryFileName = std::string(history_file_name); + + LLConversation conversation(params); + mConversations.push_back(conversation); + } + fclose(fp); + + notifyObservers(); + return true; +} + +void LLConversationLog::notifyObservers() +{ + std::set::const_iterator iter = mObservers.begin(); + for (; iter != mObservers.end(); ++iter) + { + (*iter)->changed(); + } +} diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h new file mode 100644 index 0000000000..700472ca8b --- /dev/null +++ b/indra/newview/llconversationlog.h @@ -0,0 +1,164 @@ +/** + * @file llconversationlog.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LLCONVERSATIONLOG_H_ +#define LLCONVERSATIONLOG_H_ + +#include "llcallingcard.h" +#include "llimfloater.h" +#include "llimview.h" + +class LLConversationLogObserver; +class Conversation_params; + +typedef LLIMModel::LLIMSession::SType SessionType; + +/* + * This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data. + * Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem. + */ +class LLConversation +{ +public: + + LLConversation(const Conversation_params& params); + LLConversation(const LLIMModel::LLIMSession& session); + LLConversation(const LLConversation& conversation); + + ~LLConversation(); + + const SessionType& getConversationType() const { return mConversationType; } + const std::string& getConversationName() const { return mConversationName; } + const std::string& getHistoryFileName() const { return mHistoryFileName; } + const LLUUID& getSessionID() const { return mSessionID; } + const LLUUID& getParticipantID() const { return mParticipantID; } + const std::string& getTimestamp() const { return mTimestamp; } + const time_t& getTime() const { return mTime; } + bool isVoice() const { return mIsVoice; } + bool hasOfflineMessages() const { return mHasOfflineIMs; } + + /* + * Resets flag of unread offline message to false when im floater with this conversation is opened. + */ + void onIMFloaterShown(const LLUUID& session_id); + + /* + * returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started + */ + static const std::string createTimestamp(const time_t& utc_time); + +private: + + /* + * If conversation has unread offline messages sets callback for opening LLIMFloater + * with this conversation. + */ + void setListenIMFloaterOpened(); + + boost::signals2::connection mIMFloaterShowedConnection; + + time_t mTime; // start time of conversation + SessionType mConversationType; + std::string mConversationName; + std::string mHistoryFileName; + LLUUID mSessionID; + LLUUID mParticipantID; + bool mIsVoice; + bool mHasOfflineIMs; + std::string mTimestamp; // conversation start time in form of: mm/dd/yyyy hh:mm +}; + +/** + * LLConversationLog stores all agent's conversations. + * This class is responsible for creating and storing LLConversation objects when im or voice session starts. + * Also this class saves/retrieves conversations to/from file. + * + * Also please note that it may be several conversations with the same sessionID stored in the conversation log. + * To distinguish two conversations with the same sessionID it's also needed to compare their creation date. + */ + +class LLConversationLog : public LLSingleton, LLIMSessionObserver, LLFriendObserver +{ + friend class LLSingleton; +public: + + /** + * adds conversation to the conversation list and notifies observers + */ + void logConversation(const LLConversation& conversation); + void removeConversation(const LLConversation& conversation); + + /** + * Returns first conversation with matched session_id + */ + const LLConversation* getConversation(const LLUUID& session_id); + + void addObserver(LLConversationLogObserver* observer); + void removeObserver(LLConversationLogObserver* observer); + + const std::vector& getConversations() { return mConversations; } + + // LLIMSessionObserver triggers + virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id); + virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){} // Stub + virtual void sessionRemoved(const LLUUID& session_id){} // Stub + virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){} // Stub + + // LLFriendObserver trigger + virtual void changed(U32 mask); + + /** + * public method which is called on viewer exit to save conversation log + */ + void cache(); + +private: + + LLConversationLog(); + void notifyObservers(); + + /** + * constructs file name in which conversations log will be saved + * file name template: agentID.call_log. + * For example: a086icaa-782d-88d0-ae29-987a55c99sss.call_log + */ + std::string getFileName(); + + bool saveToFile(const std::string& filename); + bool loadFromFile(const std::string& filename); + + typedef std::vector conversations_vec_t; + std::vector mConversations; + std::set mObservers; +}; + +class LLConversationLogObserver +{ +public: + virtual ~LLConversationLogObserver(){} + virtual void changed() = 0; +}; + +#endif /* LLCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp new file mode 100644 index 0000000000..0433719a89 --- /dev/null +++ b/indra/newview/llconversationloglist.cpp @@ -0,0 +1,422 @@ +/** + * @file llconversationloglist.cpp + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llavataractions.h" +#include "llagent.h" +#include "llfloaterreg.h" +#include "llfloaterconversationpreview.h" +#include "llgroupactions.h" +#include "llconversationloglist.h" +#include "llconversationloglistitem.h" +#include "llviewermenu.h" + +static LLDefaultChildRegistry::Register r("conversation_log_list"); + +static LLConversationLogListNameComparator NAME_COMPARATOR; +static LLConversationLogListDateComparator DATE_COMPARATOR; + +LLConversationLogList::LLConversationLogList(const Params& p) +: LLFlatListViewEx(p), + mIsDirty(true) +{ + LLConversationLog::instance().addObserver(this); + + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar check_registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add ("Calllog.Action", boost::bind(&LLConversationLogList::onCustomAction, this, _2)); + check_registrar.add ("Calllog.Check", boost::bind(&LLConversationLogList::isActionChecked,this, _2)); + enable_registrar.add("Calllog.Enable", boost::bind(&LLConversationLogList::isActionEnabled,this, _2)); + + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile( + "menu_conversation_log_gear.xml", + gMenuHolder, + LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + { + mContextMenu = context_menu->getHandle(); + } + + mIsFriendsOnTop = gSavedSettings.getBOOL("SortFriendsFirst"); +} + +LLConversationLogList::~LLConversationLogList() +{ + if (mContextMenu.get()) + { + mContextMenu.get()->die(); + } + + LLConversationLog::instance().removeObserver(this); +} + +void LLConversationLogList::draw() +{ + if (mIsDirty) + { + refresh(); + } + LLFlatListViewEx::draw(); +} + +BOOL LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); + + LLToggleableMenu* context_menu = mContextMenu.get(); + { + context_menu->buildDrawLabels(); + if (context_menu && size()) + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } + + return handled; +} + +void LLConversationLogList::setNameFilter(const std::string& filter) +{ + std::string filter_upper = filter; + LLStringUtil::toUpper(filter_upper); + if (mNameFilter != filter_upper) + { + mNameFilter = filter_upper; + setDirty(); + } +} + +bool LLConversationLogList::findInsensitive(std::string haystack, const std::string& needle_upper) +{ + LLStringUtil::toUpper(haystack); + return haystack.find(needle_upper) != std::string::npos; +} + +void LLConversationLogList::sortByName() +{ + setComparator(&NAME_COMPARATOR); + sort(); +} + +void LLConversationLogList::sortByDate() +{ + setComparator(&DATE_COMPARATOR); + sort(); +} + +void LLConversationLogList::toggleSortFriendsOnTop() +{ + mIsFriendsOnTop = !mIsFriendsOnTop; + gSavedSettings.setBOOL("SortFriendsFirst", mIsFriendsOnTop); + sort(); +} + +void LLConversationLogList::changed() +{ + refresh(); +} + +void LLConversationLogList::addNewItem(const LLConversation* conversation) +{ + LLConversationLogListItem* item = new LLConversationLogListItem(&*conversation); + if (!mNameFilter.empty()) + { + item->highlightNameDate(mNameFilter); + } + addItem(item, conversation->getSessionID(), ADD_TOP); +} + +void LLConversationLogList::refresh() +{ + rebuildList(); + sort(); + + mIsDirty = false; +} + +void LLConversationLogList::rebuildList() +{ + clear(); + + bool have_filter = !mNameFilter.empty(); + + const std::vector& conversations = LLConversationLog::instance().getConversations(); + std::vector::const_iterator iter = conversations.begin(); + + for (; iter != conversations.end(); ++iter) + { + bool not_found = have_filter && !findInsensitive(iter->getConversationName(), mNameFilter) && !findInsensitive(iter->getTimestamp(), mNameFilter); + if (not_found) + continue; + + addNewItem(&*iter); + } +} + +void LLConversationLogList::onCustomAction(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + + if ("im" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startIM(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startIM(selected_id); + break; + + default: + break; + } + } + else if ("call" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::startCall(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::startCall(selected_id); + break; + + default: + break; + } + } + else if ("view_profile" == command_name) + { + switch (stype) + { + case LLIMModel::LLIMSession::P2P_SESSION: + LLAvatarActions::showProfile(selected_id); + break; + + case LLIMModel::LLIMSession::GROUP_SESSION: + LLGroupActions::show(selected_id); + break; + + default: + break; + } + } + else if ("chat_history" == command_name) + { + const LLUUID& session_id = getSelectedConversation()->getSessionID(); + LLFloaterReg::showInstance("preview_conversation", session_id, true); + } + else if ("offer_teleport" == command_name) + { + LLAvatarActions::offerTeleport(selected_id); + } + else if("add_rem_friend" == command_name) + { + if (LLAvatarActions::isFriend(selected_id)) + { + LLAvatarActions::removeFriendDialog(selected_id); + } + else + { + LLAvatarActions::requestFriendshipDialog(selected_id); + } + } + else if ("invite_to_group" == command_name) + { + LLAvatarActions::inviteToGroup(selected_id); + } + else if ("show_on_map" == command_name) + { + LLAvatarActions::showOnMap(selected_id); + } + else if ("share" == command_name) + { + LLAvatarActions::share(selected_id); + } + else if ("pay" == command_name) + { + LLAvatarActions::pay(selected_id); + } + else if ("block" == command_name) + { + LLAvatarActions::toggleBlock(selected_id); + } +} + +bool LLConversationLogList::isActionEnabled(const LLSD& userdata) +{ + if (numSelected() != 1) + { + return false; + } + + const std::string command_name = userdata.asString(); + + LLIMModel::LLIMSession::SType stype = getSelectedSessionType(); + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == stype; + bool is_group = LLIMModel::LLIMSession::GROUP_SESSION == stype; + + if ("can_im" == command_name || "can_view_profile" == command_name) + { + return is_p2p || is_group; + } + else if ("can_view_chat_history" == command_name) + { + return true; + } + else if ("can_call" == command_name) + { + return (is_p2p || is_group) && LLAvatarActions::canCall(); + } + else if ("add_rem_friend" == command_name || + "can_invite_to_group" == command_name || + "can_share" == command_name || + "can_block" == command_name || + "can_pay" == command_name) + { + return is_p2p; + } + else if("can_offer_teleport" == command_name) + { + return is_p2p && LLAvatarActions::canOfferTeleport(selected_id); + } + else if ("can_show_on_map") + { + return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike()); + } + + return false; +} + +bool LLConversationLogList::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + const LLUUID& selected_id = getSelectedConversation()->getParticipantID(); + bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == getSelectedSessionType(); + + if ("is_blocked" == command_name) + { + return is_p2p && LLAvatarActions::isBlocked(selected_id); + } + else if ("is_friend" == command_name) + { + return is_p2p && LLAvatarActions::isFriend(selected_id); + } + + return false; +} + +LLIMModel::LLIMSession::SType LLConversationLogList::getSelectedSessionType() +{ + const LLConversationLogListItem* item = getSelectedConversationPanel(); + + if (item) + { + return item->getConversation()->getConversationType(); + } + + return LLIMModel::LLIMSession::NONE_SESSION; +} + +const LLConversationLogListItem* LLConversationLogList::getSelectedConversationPanel() +{ + LLPanel* panel = LLFlatListViewEx::getSelectedItem(); + LLConversationLogListItem* conv_panel = dynamic_cast(panel); + + return conv_panel; +} + +const LLConversation* LLConversationLogList::getSelectedConversation() +{ + const LLConversationLogListItem* panel = getSelectedConversationPanel(); + + if (panel) + { + return panel->getConversation(); + } + + return NULL; +} + +bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLConversationLogListItem* conversation_item1 = dynamic_cast(item1); + const LLConversationLogListItem* conversation_item2 = dynamic_cast(item2); + + if (!conversation_item1 || !conversation_item2) + { + llerror("conversation_item1 and conversation_item2 cannot be null", 0); + return true; + } + + return doCompare(conversation_item1, conversation_item2); +} + +bool LLConversationLogListNameComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + std::string name1 = conversation1->getConversation()->getConversationName(); + std::string name2 = conversation2->getConversation()->getConversationName(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return name1 < name2; +} + +bool LLConversationLogListDateComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const +{ + time_t date1 = conversation1->getConversation()->getTime(); + time_t date2 = conversation2->getConversation()->getTime(); + const LLUUID& id1 = conversation1->getConversation()->getParticipantID(); + const LLUUID& id2 = conversation2->getConversation()->getParticipantID(); + + bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst"); + if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2))) + { + return LLAvatarActions::isFriend(id1); + } + + return date1 > date2; +} diff --git a/indra/newview/llconversationloglist.h b/indra/newview/llconversationloglist.h new file mode 100644 index 0000000000..dff34a74c6 --- /dev/null +++ b/indra/newview/llconversationloglist.h @@ -0,0 +1,143 @@ +/** + * @file llconversationloglist.h + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LLCONVERSATIONLOGLIST_H_ +#define LLCONVERSATIONLOGLIST_H_ + +#include "llconversationlog.h" +#include "llflatlistview.h" +#include "lltoggleablemenu.h" + +class LLConversationLogListItem; + +/** + * List of all agent's conversations. I.e. history of conversations. + * This list represents contents of the LLConversationLog. + * Each change in LLConversationLog leads to rebuilding this list, so + * it's always in actual state. + */ + +class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogObserver +{ + LOG_CLASS(LLConversationLogList); +public: + struct Params : public LLInitParam::Block + { + Params(){}; + }; + + LLConversationLogList(const Params& p); + virtual ~LLConversationLogList(); + + virtual void draw(); + + virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + + LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); } + + void addNewItem(const LLConversation* conversation); + void setNameFilter(const std::string& filter); + void sortByName(); + void sortByDate(); + void toggleSortFriendsOnTop(); + bool getSortFriendsOnTop() const { return mIsFriendsOnTop; } + + /** + * Changes from LLConversationLogObserver + */ + virtual void changed(); + +private: + + void setDirty(bool dirty = true) { mIsDirty = dirty; } + void refresh(); + + /** + * Clears list and re-adds items from LLConverstationLog + * If filter is not empty re-adds items which match the filter + */ + void rebuildList(); + + bool findInsensitive(std::string haystack, const std::string& needle_upper); + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + LLIMModel::LLIMSession::SType getSelectedSessionType(); + const LLConversationLogListItem* getSelectedConversationPanel(); + const LLConversation* getSelectedConversation(); + + LLHandle mContextMenu; + bool mIsDirty; + bool mIsFriendsOnTop; + std::string mNameFilter; +}; + +/** + * Abstract comparator for ConversationLogList items + */ +class LLConversationLogListItemComparator : public LLFlatListView::ItemComparator +{ + LOG_CLASS(LLConversationLogListItemComparator); + +public: + LLConversationLogListItemComparator() {}; + virtual ~LLConversationLogListItemComparator() {}; + + virtual bool compare(const LLPanel* item1, const LLPanel* item2) const; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const = 0; +}; + +class LLConversationLogListNameComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListNameComparator); + +public: + LLConversationLogListNameComparator() {}; + virtual ~LLConversationLogListNameComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +class LLConversationLogListDateComparator : public LLConversationLogListItemComparator +{ + LOG_CLASS(LLConversationLogListDateComparator); + +public: + LLConversationLogListDateComparator() {}; + virtual ~LLConversationLogListDateComparator() {}; + +protected: + + virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const; +}; + +#endif /* LLCONVERSATIONLOGLIST_H_ */ diff --git a/indra/newview/llconversationloglistitem.cpp b/indra/newview/llconversationloglistitem.cpp new file mode 100644 index 0000000000..fc2e757864 --- /dev/null +++ b/indra/newview/llconversationloglistitem.cpp @@ -0,0 +1,157 @@ +/** + * @file llconversationloglistitem.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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" + +// llui +#include "lliconctrl.h" +#include "lltextbox.h" +#include "lltextutil.h" + +// newview +#include "llavatariconctrl.h" +#include "llconversationlog.h" +#include "llconversationloglistitem.h" +#include "llgroupiconctrl.h" +#include "llinventoryicon.h" + +LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conversation) +: LLPanel(), + mConversation(conversation), + mConversationName(NULL), + mConversationDate(NULL) +{ + buildFromFile("panel_conversation_log_list_item.xml"); + + LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID()); + + bool has_offline_ims = !mConversation->isVoice() && mConversation->hasOfflineMessages(); + bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus(); + + if (has_offline_ims && !ims_are_read) + { + mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1)); + } +} + +LLConversationLogListItem::~LLConversationLogListItem() +{ + mIMFloaterShowedConnection.disconnect(); +} + +BOOL LLConversationLogListItem::postBuild() +{ + initIcons(); + + // set conversation name + mConversationName = getChild("conversation_name"); + mConversationName->setValue(mConversation->getConversationName()); + + // set conversation date and time + mConversationDate = getChild("date_time"); + mConversationDate->setValue(mConversation->getTimestamp()); + + getChild("delete_btn")->setClickedCallback(boost::bind(&LLConversationLogListItem::onRemoveBtnClicked, this)); + + return TRUE; +} + +void LLConversationLogListItem::initIcons() +{ + switch (mConversation->getConversationType()) + { + case LLIMModel::LLIMSession::P2P_SESSION: + case LLIMModel::LLIMSession::ADHOC_SESSION: + { + LLAvatarIconCtrl* avatar_icon = getChild("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mConversation->getParticipantID()); + break; + } + case LLIMModel::LLIMSession::GROUP_SESSION: + { + LLGroupIconCtrl* group_icon = getChild("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mConversation->getSessionID()); + break; + } + default: + break; + } + + if (mConversation->isVoice()) + { + getChild("voice_session_icon")->setVisible(TRUE); + } + else + { + if (mConversation->hasOfflineMessages()) + { + getChild("unread_ims_icon")->setVisible(TRUE); + } + } +} + +void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(true); + LLPanel::onMouseEnter(x, y, mask); +} + +void LLConversationLogListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ + getChildView("hovered_icon")->setVisible(false); + LLPanel::onMouseLeave(x, y, mask); +} + +void LLConversationLogListItem::setValue(const LLSD& value) +{ + if (!value.isMap() || !value.has("selected")) + { + return; + } + + getChildView("selected_icon")->setVisible(value["selected"]); +} + +void LLConversationLogListItem::onIMFloaterShown(const LLUUID& session_id) +{ + if (mConversation->getSessionID() == session_id) + { + getChild("unread_ims_icon")->setVisible(FALSE); + } +} + +void LLConversationLogListItem::onRemoveBtnClicked() +{ + LLConversationLog::instance().removeConversation(*mConversation); +} + +void LLConversationLogListItem::highlightNameDate(const std::string& highlited_text) +{ + LLStyle::Params params; + LLTextUtil::textboxSetHighlightedVal(mConversationName, params, mConversation->getConversationName(), highlited_text); + LLTextUtil::textboxSetHighlightedVal(mConversationDate, params, mConversation->getTimestamp(), highlited_text); +} diff --git a/indra/newview/llconversationloglistitem.h b/indra/newview/llconversationloglistitem.h new file mode 100644 index 0000000000..deba7d4563 --- /dev/null +++ b/indra/newview/llconversationloglistitem.h @@ -0,0 +1,77 @@ +/** + * @file llconversationloglistitem.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LLCONVERSATIONLOGLISTITEM_H_ +#define LLCONVERSATIONLOGLISTITEM_H_ + +#include "llimfloater.h" +#include "llpanel.h" + +class LLTextBox; +class LLConversation; + +/** + * This class is a visual representation of LLConversation, each of which is LLConversationLog entry. + * LLConversationLogList consists of these LLConversationLogListItems. + * LLConversationLogListItem consists of: + * conversaion_type_icon + * conversaion_name + * conversaion_date + * Also LLConversationLogListItem holds pointer to its LLConversationLog. + */ + +class LLConversationLogListItem : public LLPanel +{ +public: + LLConversationLogListItem(const LLConversation* conversation); + virtual ~LLConversationLogListItem(); + + void onMouseEnter(S32 x, S32 y, MASK mask); + void onMouseLeave(S32 x, S32 y, MASK mask); + + virtual void setValue(const LLSD& value); + + virtual BOOL postBuild(); + + void onIMFloaterShown(const LLUUID& session_id); + void onRemoveBtnClicked(); + + const LLConversation* getConversation() const { return mConversation; } + + void highlightNameDate(const std::string& highlited_text); + +private: + + void initIcons(); + + const LLConversation* mConversation; + + LLTextBox* mConversationName; + LLTextBox* mConversationDate; + + boost::signals2::connection mIMFloaterShowedConnection; +}; + +#endif /* LLCONVERSATIONLOGITEM_H_ */ diff --git a/indra/newview/llfloaterconversationlog.cpp b/indra/newview/llfloaterconversationlog.cpp new file mode 100644 index 0000000000..569ba12ed6 --- /dev/null +++ b/indra/newview/llfloaterconversationlog.cpp @@ -0,0 +1,127 @@ +/** + * @file llfloaterconversationlog.cpp + * @brief Functionality of the "conversation log" floater + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llconversationloglist.h" +#include "llfiltereditor.h" +#include "llfloaterconversationlog.h" +#include "llmenubutton.h" + +LLFloaterConversationLog::LLFloaterConversationLog(const LLSD& key) +: LLFloater(key), + mConversationLogList(NULL) +{ + mCommitCallbackRegistrar.add("CallLog.Action", boost::bind(&LLFloaterConversationLog::onCustomAction, this, _2)); + mEnableCallbackRegistrar.add("CallLog.Check", boost::bind(&LLFloaterConversationLog::isActionChecked, this, _2)); +} + +BOOL LLFloaterConversationLog::postBuild() +{ + mConversationLogList = getChild("conversation_log_list"); + + switch (gSavedSettings.getU32("CallLogSortOrder")) + { + case E_SORT_BY_NAME: + mConversationLogList->sortByName(); + break; + + case E_SORT_BY_DATE: + mConversationLogList->sortByDate(); + break; + } + + // Use the context menu of the Conversation list for the Conversation tab gear menu. + LLToggleableMenu* conversations_gear_menu = mConversationLogList->getContextMenu(); + if (conversations_gear_menu) + { + getChild("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + + getChild("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2)); + + return LLFloater::postBuild(); +} + +void LLFloaterConversationLog::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationLog::onFilterEdit(const std::string& search_string) +{ + std::string filter = search_string; + LLStringUtil::trimHead(filter); + + mConversationLogList->setNameFilter(filter); +} + + +void LLFloaterConversationLog::onCustomAction (const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + if ("sort_by_name" == command_name) + { + mConversationLogList->sortByName(); + gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_date" == command_name) + { + mConversationLogList->sortByDate(); + gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_DATE); + } + else if ("sort_friends_on_top" == command_name) + { + mConversationLogList->toggleSortFriendsOnTop(); + } +} + +bool LLFloaterConversationLog::isActionEnabled(const LLSD& userdata) +{ + return true; +} + +bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata) +{ + const std::string command_name = userdata.asString(); + + U32 sort_order = gSavedSettings.getU32("CallLogSortOrder"); + + if ("sort_by_name" == command_name) + { + return sort_order == E_SORT_BY_NAME; + } + else if ("sort_by_date" == command_name) + { + return sort_order == E_SORT_BY_DATE; + } + else if ("sort_friends_on_top" == command_name) + { + return gSavedSettings.getBOOL("SortFriendsFirst"); + } + + return false; +} diff --git a/indra/newview/llfloaterconversationlog.h b/indra/newview/llfloaterconversationlog.h new file mode 100644 index 0000000000..40dd266663 --- /dev/null +++ b/indra/newview/llfloaterconversationlog.h @@ -0,0 +1,61 @@ +/** + * @file llfloaterconversationlog.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LL_LLFLOATERCONVERSATIONLOG_H_ +#define LL_LLFLOATERCONVERSATIONLOG_H_ + +#include "llfloater.h" + +class LLConversationLogList; + +class LLFloaterConversationLog : public LLFloater +{ +public: + + typedef enum e_sort_oder{ + E_SORT_BY_NAME = 0, + E_SORT_BY_DATE = 1, + } ESortOrder; + + LLFloaterConversationLog(const LLSD& key); + virtual ~LLFloaterConversationLog(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + + void onFilterEdit(const std::string& search_string); + +private: + + void onCustomAction (const LLSD& userdata); + bool isActionEnabled(const LLSD& userdata); + bool isActionChecked(const LLSD& userdata); + + LLConversationLogList* mConversationLogList; +}; + + +#endif /* LLFLOATERCONVERSATIONLOG_H_ */ diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp new file mode 100644 index 0000000000..e8554bb066 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.cpp @@ -0,0 +1,112 @@ +/** + * @file llfloaterconversationpreview.cpp + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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 "llconversationlog.h" +#include "llfloaterconversationpreview.h" +#include "llimview.h" +#include "lllineeditor.h" + +LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id) +: LLFloater(session_id), + mChatHistory(NULL), + mSessionID(session_id.asUUID()) +{} + +BOOL LLFloaterConversationPreview::postBuild() +{ + mChatHistory = getChild("chat_history"); + + const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID); + if (conv) + { + std::string name = conv->getConversationName(); + LLStringUtil::format_map_t args; + args["[NAME]"] = name; + std::string title = getString("Title", args); + setTitle(title); + + getChild("description")->setValue(name); + } + + return LLFloater::postBuild(); +} + +void LLFloaterConversationPreview::draw() +{ + LLFloater::draw(); +} + +void LLFloaterConversationPreview::onOpen(const LLSD& session_id) +{ + const LLConversation* conv = LLConversationLog::instance().getConversation(session_id); + if (!conv) + { + return; + } + std::list messages; + std::string file = conv->getHistoryFileName(); + LLLogChat::loadAllHistory(file, messages); + + if (messages.size()) + { + std::ostringstream message; + std::list::const_iterator iter = messages.begin(); + for (; iter != messages.end(); ++iter) + { + LLSD msg = *iter; + + std::string time = msg["time"].asString(); + LLUUID from_id = msg["from_id"].asUUID(); + std::string from = msg["from"].asString(); + std::string message = msg["message"].asString(); + bool is_history = msg["is_history"].asBoolean(); + + LLChat chat; + chat.mFromID = from_id; + chat.mSessionID = session_id; + chat.mFromName = from; + chat.mTimeStr = time; + chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle; + chat.mText = message; + + appendMessage(chat); + } + } +} + +void LLFloaterConversationPreview::appendMessage(const LLChat& chat) +{ + if (!chat.mMuted) + { + LLSD args; + args["use_plain_text_chat_history"] = true; + args["show_time"] = true; + args["show_names_for_p2p_conv"] = true; + + mChatHistory->appendMessage(chat); + } +} diff --git a/indra/newview/llfloaterconversationpreview.h b/indra/newview/llfloaterconversationpreview.h new file mode 100644 index 0000000000..cfc7c34485 --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.h @@ -0,0 +1,51 @@ +/** + * @file llfloaterconversationpreview.h + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef LLFLOATERCONVERSATIONPREVIEW_H_ +#define LLFLOATERCONVERSATIONPREVIEW_H_ + +#include "llchathistory.h" +#include "llfloater.h" + +class LLFloaterConversationPreview : public LLFloater +{ +public: + + LLFloaterConversationPreview(const LLSD& session_id); + virtual ~LLFloaterConversationPreview(){}; + + virtual BOOL postBuild(); + + virtual void draw(); + virtual void onOpen(const LLSD& session_id); + +private: + void appendMessage(const LLChat& chat); + + LLChatHistory* mChatHistory; + LLUUID mSessionID; +}; + +#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */ diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 22ce3cd42b..f1d7d1c04f 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -59,6 +59,8 @@ #include "llviewerchat.h" #include "llnotificationmanager.h" +floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal; + LLIMFloater::LLIMFloater(const LLUUID& session_id) : LLIMConversation(session_id), mLastMessageIndex(-1), @@ -765,6 +767,11 @@ void LLIMFloater::setVisible(BOOL visible) chiclet->setToggleState(false); } } + + if (visible) + { + sIMFloaterShowedSignal(mSessionID); + } } BOOL LLIMFloater::getVisible() @@ -1334,3 +1341,8 @@ void LLIMFloater::addToHost(const LLUUID& session_id) } } } + +boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb) +{ + return LLIMFloater::sIMFloaterShowedSignal.connect(cb); +} diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 2ac11ded20..7e45cf42c2 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -44,6 +44,8 @@ class LLChatHistory; class LLInventoryItem; class LLInventoryCategory; +typedef boost::signals2::signal floater_showed_signal_t; + /** * Individual IM window that appears at the bottom of the screen, * optionally "docked" to the bottom tray. @@ -125,7 +127,11 @@ public: bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; } + static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb); + static floater_showed_signal_t sIMFloaterShowedSignal; + private: + // process focus events to set a currently active session /* virtual */ void onFocusLost(); /* virtual */ void onFocusReceived(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index cdbb7c7cca..c66c0cd865 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -175,10 +175,11 @@ LLIMModel::LLIMModel() addNewMsgCallback(boost::bind(&toast_callback, _1)); } -LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) +LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) : mSessionID(session_id), mName(name), mType(type), + mHasOfflineMessage(has_offline_msg), mParticipantUnreadMessageCount(0), mNumUnread(0), mOtherParticipantID(other_participant_id), @@ -375,6 +376,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES break; } } + default: + break; } // Update speakers list when connected if (LLVoiceChannel::STATE_CONNECTED == new_state) @@ -676,7 +679,7 @@ void LLIMModel::testMessages() //session name should not be empty bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice) + const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg) { if (name.empty()) { @@ -690,7 +693,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co return false; } - LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice); + LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg); mId2SessionMap[session_id] = session; // When notifying observer, name of session is used instead of "name", because they may not be the @@ -702,10 +705,10 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co } -bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice) +bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg) { uuid_vec_t no_ids; - return newSession(session_id, name, type, other_participant_id, no_ids, voice); + return newSession(session_id, name, type, other_participant_id, no_ids, voice, has_offline_msg); } bool LLIMModel::clearSession(const LLUUID& session_id) @@ -2398,6 +2401,7 @@ void LLIMMgr::addMessage( const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg, const std::string& session_name, EInstantMessage dialog, U32 parent_estate_id, @@ -2423,7 +2427,7 @@ void LLIMMgr::addMessage( bool new_session = !hasSession(new_session_id); if (new_session) { - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); + LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg); // When we get a new IM, and if you are a god, display a bit // of information about the source. This is to help liaisons @@ -3315,6 +3319,7 @@ public: from_id, name, buffer, + IM_OFFLINE == offline, std::string((char*)&bin_bucket[0]), IM_SESSION_INVITE, message_params["parent_estate_id"].asInteger(), diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 80bf315aa8..fa9d20ca53 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -70,10 +70,11 @@ public: GROUP_SESSION, ADHOC_SESSION, AVALINE_SESSION, + NONE_SESSION, } SType; LLIMSession(const LLUUID& session_id, const std::string& name, - const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice); + const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg); virtual ~LLIMSession(); void sessionInitReplyReceived(const LLUUID& new_session_id); @@ -133,6 +134,8 @@ public: //if IM session is created for a voice call bool mStartedAsIMCall; + bool mHasOfflineMessage; + private: void onAdHocNameCache(const LLAvatarName& av_name); @@ -181,10 +184,10 @@ public: * @param name session name should not be empty, will return false if empty */ bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, - const uuid_vec_t& ids, bool voice = false); + const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false); bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, - const LLUUID& other_participant_id, bool voice = false); + const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false); /** * Remove all session data associated with a session specified by session_id @@ -325,6 +328,7 @@ public: const LLUUID& target_id, const std::string& from, const std::string& msg, + bool is_offline_msg = false, const std::string& session_name = LLStringUtil::null, EInstantMessage dialog = IM_NOTHING_SPECIAL, U32 parent_estate_id = 0, diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 65fd6d7019..4cf6ff55a4 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -94,6 +94,7 @@ #include "llcallingcard.h" #include "llconsole.h" #include "llcontainerview.h" +#include "llconversationlog.h" #include "lldebugview.h" #include "lldrawable.h" #include "lleventnotifier.h" @@ -1266,6 +1267,8 @@ bool idle_startup() display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); + LLConversationLog::getInstance(); + return FALSE; } diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index bf12b08321..5c662af875 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -50,6 +50,8 @@ #include "llfloaterbump.h" #include "llfloaterbvhpreview.h" #include "llfloatercamera.h" +#include "llfloaterconversationlog.h" +#include "llfloaterconversationpreview.h" #include "llfloaterdeleteenvpreset.h" #include "llfloaterdisplayname.h" #include "llfloatereditdaycycle.h" @@ -188,6 +190,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -253,6 +256,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); + LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 03c113ecb3..20887f7832 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -2396,6 +2396,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2435,7 +2436,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if (!gIMMgr->isNonFriendSessionNotified(session_id)) { std::string message = LLTrans::getString("IM_unblock_only_groups_friends"); - gIMMgr->addMessage(session_id, from_id, name, message); + gIMMgr->addMessage(session_id, from_id, name, message, IM_OFFLINE == offline); gIMMgr->addNotifiedNonFriendSessionID(session_id); } @@ -2448,6 +2449,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2788,6 +2790,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, ll_safe_string((char*)binary_bucket), IM_SESSION_INVITE, parent_estate_id, diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index df1d3f2955..f4c88403a5 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -4072,6 +4072,7 @@ void LLVivoxVoiceClient::messageEvent( session->mCallerID, session->mName.c_str(), message.c_str(), + false, LLStringUtil::null, // default arg IM_NOTHING_SPECIAL, // default arg 0, // default arg diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml new file mode 100644 index 0000000000..1c5800e25f --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/floater_conversation_preview.xml b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml new file mode 100644 index 0000000000..27b744aefb --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml @@ -0,0 +1,53 @@ + + + + CONVERSATION: [NAME] + + + Description: + + + + + diff --git a/indra/newview/skins/default/xui/en/floater_im_container.xml b/indra/newview/skins/default/xui/en/floater_im_container.xml index e5ef80e352..e8ef3c1df9 100644 --- a/indra/newview/skins/default/xui/en/floater_im_container.xml +++ b/indra/newview/skins/default/xui/en/floater_im_container.xml @@ -56,6 +56,7 @@ image_overlay="Conv_toolbar_sort" image_selected="Toolbar_Middle_Selected" image_unselected="Toolbar_Middle_Off" + menu_filename="menu_participant_view.xml" layout="topleft" left="10" name="sort_btn" diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml new file mode 100644 index 0000000000..b8d0eef956 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml new file mode 100644 index 0000000000..4ab8cb4f7d --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_conversation_log_view.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/menu_participant_view.xml b/indra/newview/skins/default/xui/en/menu_participant_view.xml new file mode 100644 index 0000000000..6401b0e3b7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml new file mode 100644 index 0000000000..3c98e32e7d --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml @@ -0,0 +1,108 @@ + + + + + + + + + + +