diff options
Diffstat (limited to 'indra/newview/llappviewer.cpp')
| -rw-r--r-- | indra/newview/llappviewer.cpp | 779 |
1 files changed, 577 insertions, 202 deletions
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 2c203869c7..05be94b488 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -131,10 +131,10 @@ #include "stringize.h" #include "llcoros.h" #include "llexception.h" -#if LL_DARWIN || LL_LINUX || __FreeBSD__ +#if !_M_ARM64 // !LL_LINUX #include "cef/dullahan_version.h" -#endif #include "vlc/libvlc_version.h" +#endif // LL_LINUX #if LL_DARWIN #if LL_SDL @@ -220,7 +220,6 @@ #include "llfloatersimplesnapshot.h" #include "llfloatersnapshot.h" #include "llsidepanelinventory.h" -#include "llatmosphere.h" // includes for idle() idleShutdown() #include "llviewercontrol.h" @@ -255,6 +254,9 @@ using namespace LL; #include "llviewereventrecorder.h" #include <chrono> +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.h" // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either @@ -275,6 +277,16 @@ using namespace LL; #pragma warning (disable:4702) #endif +#ifdef LL_DISCORD +#define DISCORDPP_IMPLEMENTATION +#include <discordpp.h> +static std::shared_ptr<discordpp::Client> gDiscordClient; +static uint64_t gDiscordTimestampsStart; +static std::string gDiscordActivityDetails; +static int32_t gDiscordPartyCurrentSize; +static int32_t gDiscordPartyMaxSize; +#endif + static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor @@ -292,6 +304,7 @@ extern bool gDebugGL; #if LL_DARWIN extern bool gHiDPISupport; +extern bool gHDRDisplaySupport; #endif //////////////////////////////////////////////////////////// @@ -358,10 +371,6 @@ std::string gLastVersionChannel; LLVector3 gWindVec(3.0, 3.0, 0.0); LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); -U32 gPacketsIn = 0; - -bool gPrintMessagesThisFrame = false; - bool gRandomizeFramerate = false; bool gPeriodicSlowFrame = false; @@ -370,6 +379,7 @@ bool gLLErrorActivated = false; bool gLogoutInProgress = false; bool gSimulateMemLeak = false; +bool gDoDisconnect = false; // We don't want anyone, especially threads working on the graphics pipeline, // to have to block due to this WorkQueue being full. @@ -383,7 +393,6 @@ const std::string MARKER_FILE_NAME("SecondLife.exec_marker"); const std::string START_MARKER_FILE_NAME("SecondLife.start_marker"); const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker"); const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static bool gDoDisconnect = false; static std::string gLaunchFileOnQuit; // Used on Win32 for other apps to identify our window (eg, win_setup) @@ -457,13 +466,28 @@ static bool app_metrics_qa_mode = false; void idle_afk_check() { + // Don't check AFK status during startup states + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return; + } + // check idle timers F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32(); - F32 afk_timeout = (F32)gSavedSettings.getS32("AFKTimeout"); - if (afk_timeout && (current_idle > afk_timeout) && ! gAgent.getAFK()) + static LLCachedControl<S32> afk_timeout(gSavedSettings, "AFKTimeout", 300); + if (afk_timeout() && (current_idle > afk_timeout())) { - LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; - gAgent.setAFK(); + if (!gAgent.getAFK()) + { + LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL; + gAgent.setAFK(); + } + else + { + // Refresh timer so that random one click or hover won't clear the status. + // But expanding the window still should lift afk status + gAwayTimer.reset(); + } } } @@ -489,7 +513,7 @@ static void deferred_ui_audio_callback(const LLUUID& uuid) bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) { - if(!match || !base || base->getPlainText()) + if (!match || match->getSkipProfileIcon() || !base || base->getPlainText()) return false; LLUUID match_id = match->getID(); @@ -572,6 +596,7 @@ static void settings_to_globals() LLWindowMacOSX::sUseMultGL = gSavedSettings.getBOOL("RenderAppleUseMultGL"); #endif // LL_SDL gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI"); + gHDRDisplaySupport = gSavedSettings.getBOOL("MPHDRDisplay"); #endif } @@ -583,6 +608,8 @@ static void settings_modify() LLVOSurfacePatch::sLODFactor = gSavedSettings.getF32("RenderTerrainLODFactor"); LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] gDebugGL = gDebugGLSession || gDebugSession; + bool noGLDebug = gSavedSettings.getBOOL("MPNoGLDebug"); + if(noGLDebug) gDebugGL = false; gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); } @@ -988,7 +1015,7 @@ bool LLAppViewer::init() return false; } -#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) +#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || _M_X64 // Without SSE2 support we will crash almost immediately, warn here. if (!gSysCPU.hasSSE2()) { @@ -1227,15 +1254,15 @@ bool LLAppViewer::init() /// Tell the Coprocedure manager how to discover and store the pool sizes // what I wanted LLCoprocedureManager::getInstance()->setPropertyMethods( - boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1), - boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS)); + std::bind(&LLControlGroup::getU32, std::ref(gSavedSettings), std::placeholders::_1), + std::bind(&LLControlGroup::declareU32, std::ref(gSavedSettings), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, LLControlVariable::PERSIST_ALWAYS)); // TODO: consider moving proxy initialization here or LLCopocedureManager after proxy initialization, may be implement // some other protection to make sure we don't use network before initializng proxy /*----------------------------------------------------------------------*/ // nat 2016-06-29 moved the following here from the former mainLoop(). - mMainloopTimeout = new LLWatchdogTimeout(); + mMainloopTimeout = new LLWatchdogTimeout("mainloop"); // Create IO Pump to use for HTTP Requests. gServicePump = new LLPumpIO(gAPRPoolp); @@ -1260,6 +1287,7 @@ bool LLAppViewer::init() LLViewerCamera::createInstance(); LL::GLTFSceneManager::createInstance(); + gSavedSettings.setU32("DebugQualityPerformance", gSavedSettings.getU32("RenderQualityPerformance")); #if LL_WINDOWS if (!mSecondInstance) @@ -1286,11 +1314,15 @@ void LLAppViewer::initMaxHeapSize() //------------------------------------------------------------------------------------------ //currently SL is built under 32-bit setting, we set its max heap size no more than 1.6 GB. - #ifndef LL_X86_64 +/* +#ifndef LL_X86_64 F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize") ; #else +*/ F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize64"); +/* #endif +*/ LLMemory::initMaxHeapSizeGB(max_heap_size_gb); } @@ -1341,15 +1373,24 @@ bool LLAppViewer::frame() bool LLAppViewer::doFrame() { + resumeMainloopTimeout("Main:doFrameStart"); + U32 fpsLimitMaxFps = (U32)gSavedSettings.getU32("MaxFPS"); - if(fpsLimitMaxFps>120) fpsLimitMaxFps=0; + if(fpsLimitMaxFps > 120) fpsLimitMaxFps = 0; using TimePoint = std::chrono::steady_clock::time_point; + U64 additionalSleepTime = 0; + TimePoint frameStartTime = std::chrono::steady_clock::now(); - U64 fpsLimitSleepFor = 0; - TimePoint fpsLimitFrameStartTime = std::chrono::steady_clock::now(); +#ifdef LL_DISCORD + { + LL_PROFILE_ZONE_NAMED("discord_callbacks"); + discordpp::RunCallbacks(); + } +#endif LL_RECORD_BLOCK_TIME(FTM_FRAME); + LL_PROFILE_GPU_ZONE("Frame"); { // and now adjust the visuals from previous frame. if(LLPerfStats::tunables.userAutoTuneEnabled && LLPerfStats::tunables.tuningFlag != LLPerfStats::Tunables::Nothing) @@ -1424,12 +1465,14 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df mainloop"); + pingMainloopTimeout("df mainloop"); // canonical per-frame event mainloop.post(newFrame); } { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df suspend"); + pingMainloopTimeout("df suspend"); // give listeners a chance to run llcoro::suspend(); // if one of our coroutines threw an uncaught exception, rethrow it now @@ -1439,30 +1482,33 @@ bool LLAppViewer::doFrame() if (!LLApp::isExiting()) { - LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df JoystickKeyboard"); - pingMainloopTimeout("Main:JoystickKeyboard"); - - // Scan keyboard for movement keys. Command keys and typing - // are handled by windows callbacks. Don't do this until we're - // done initializing. JC - if (gViewerWindow - && (gHeadlessClient || gViewerWindow->getWindow()->getVisible()) - && gViewerWindow->getActive() - && !gViewerWindow->getWindow()->getMinimized() - && LLStartUp::getStartupState() == STATE_STARTED - && (gHeadlessClient || !gViewerWindow->getShowProgress()) - && !gFocusMgr.focusLocked()) { - LLPerfStats::RecordSceneTime T (LLPerfStats::StatType_t::RENDER_IDLE); - joystick->scanJoystick(); - gKeyboard->scanKeyboard(); - gViewerInput.scanMouse(); + LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df JoystickKeyboard"); + pingMainloopTimeout("Main:JoystickKeyboard"); + + // Scan keyboard for movement keys. Command keys and typing + // are handled by windows callbacks. Don't do this until we're + // done initializing. JC + if (gViewerWindow + && (gHeadlessClient || gViewerWindow->getWindow()->getVisible()) + && gViewerWindow->getActive() + && !gViewerWindow->getWindow()->getMinimized() + && LLStartUp::getStartupState() == STATE_STARTED + && (gHeadlessClient || !gViewerWindow->getShowProgress()) + && !gFocusMgr.focusLocked()) + { + LLPerfStats::RecordSceneTime T(LLPerfStats::StatType_t::RENDER_IDLE); + joystick->scanJoystick(); + gKeyboard->scanKeyboard(); + gViewerInput.scanMouse(); + } } // Update state based on messages, user input, object idle. { { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); + pingMainloopTimeout("df idle"); // So that it will be aware of last state. pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! } @@ -1474,7 +1520,7 @@ bool LLAppViewer::doFrame() { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df resumeMainloopTimeout"); - resumeMainloopTimeout(); + resumeMainloopTimeout("df idle"); } } @@ -1489,7 +1535,7 @@ bool LLAppViewer::doFrame() } disconnectViewer(); - resumeMainloopTimeout(); + resumeMainloopTimeout("df snapshot n disconnect"); } // Render scene. @@ -1519,23 +1565,11 @@ bool LLAppViewer::doFrame() } } - if(fpsLimitMaxFps > 0) - { - auto elapsed = std::chrono::steady_clock::now() - fpsLimitFrameStartTime; - - long long fpsLimitFrameTime = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); - U64 desired_time_us = (U32)(1000000.f / fpsLimitMaxFps); - if((fpsLimitFrameTime+1000) < desired_time_us) - { - fpsLimitSleepFor = (desired_time_us - fpsLimitFrameTime - 1000) * 1.0; - } - } - { LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df pauseMainloopTimeout"); - pingMainloopTimeout("Main:Sleep"); + pingMainloopTimeout("Main:Sleep"); - pauseMainloopTimeout(); + pauseMainloopTimeout(); } // Sleep and run background threads @@ -1543,9 +1577,25 @@ bool LLAppViewer::doFrame() //LL_RECORD_BLOCK_TIME(SLEEP2); LL_PROFILE_ZONE_WARN("Sleep2"); - if(fpsLimitSleepFor) + auto elapsed = std::chrono::steady_clock::now() - frameStartTime; + long long frameTime = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count(); + + if(fpsLimitMaxFps > 0) + { + U64 desired_time_us = (U32)(1000000.f / fpsLimitMaxFps); + if((frameTime+1000) < desired_time_us) + { + additionalSleepTime = 0.92 * (F64)(desired_time_us - frameTime); + if(additionalSleepTime < 200) + { + additionalSleepTime = 0; + } + } + } + + if(additionalSleepTime > 0) { - usleep(fpsLimitSleepFor); + std::this_thread::sleep_for(std::chrono::microseconds(additionalSleepTime)); } // yield some time to the os based on command line option @@ -1641,23 +1691,29 @@ bool LLAppViewer::doFrame() LL_PROFILE_ZONE_NAMED_CATEGORY_APP("df resumeMainloopTimeout"); resumeMainloopTimeout(); } + + //swap(); + pingMainloopTimeout("Main:End"); } } if (LLApp::isExiting()) { + pingMainloopTimeout("Main:qSnapshot"); // Save snapshot for next time, if we made it through initialization if (STATE_STARTED == LLStartUp::getStartupState()) { saveFinalSnapshot(); } + pingMainloopTimeout("Main:TerminateVoice"); if (LLVoiceClient::instanceExists()) { LLVoiceClient::getInstance()->terminate(); } + pingMainloopTimeout("Main:TerminatePump"); delete gServicePump; gServicePump = NULL; @@ -1666,6 +1722,11 @@ bool LLAppViewer::doFrame() LL_INFOS() << "Exiting main_loop" << LL_ENDL; } }LLPerfStats::StatsRecorder::endFrame(); + + // Not viewer's fault if something outside frame + // pauses viewer (ex: macOS doesn't call oneFrame), + // so stop tracking on exit. + pauseMainloopTimeout(); LL_PROFILER_FRAME_END; return ! LLApp::isRunning(); @@ -1709,11 +1770,12 @@ void LLAppViewer::flushLFSIO() bool LLAppViewer::cleanup() { - LLAtmosphere::cleanupClass(); - //ditch LLVOAvatarSelf instance gAgentAvatarp = NULL; + // Sanity check to catch cases where someone forgot to do an RlvActions::isRlvEnabled() check + LL_ERRS_IF(!RlvHandler::isEnabled() && RlvHandler::instanceExists()) << "RLV handler instance exists even though RLVa is disabled" << LL_ENDL; + LLNotifications::instance().clear(); // workaround for DEV-35406 crash on shutdown @@ -1882,36 +1944,6 @@ bool LLAppViewer::cleanup() // Clean up before GL is shut down because we might be holding on to objects with texture references LLSelectMgr::cleanupGlobals(); - LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; - - // Shut down OpenGL - if( gViewerWindow) - { - gViewerWindow->shutdownGL(); - - // Destroy window, and make sure we're not fullscreen - // This may generate window reshape and activation events. - // Therefore must do this before destroying the message system. - delete gViewerWindow; - gViewerWindow = NULL; - LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; - } - - LLSplashScreen::show(); - LLSplashScreen::update(LLTrans::getString("ShuttingDown")); - - LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; - - // viewer UI relies on keyboard so keep it aound until viewer UI isa gone - delete gKeyboard; - gKeyboard = NULL; - - if (LLViewerJoystick::instanceExists()) - { - // Turn off Space Navigator and similar devices - LLViewerJoystick::getInstance()->terminate(); - } - LL_INFOS() << "Cleaning up Objects" << LL_ENDL; LLViewerObject::cleanupVOClasses(); @@ -2072,6 +2104,36 @@ bool LLAppViewer::cleanup() sTextureFetch->shutDownTextureCacheThread() ; LLLFSThread::sLocal->shutdown(); + LL_INFOS() << "Shutting down OpenGL" << LL_ENDL; + + // Shut down OpenGL + if (gViewerWindow) + { + gViewerWindow->shutdownGL(); + + // Destroy window, and make sure we're not fullscreen + // This may generate window reshape and activation events. + // Therefore must do this before destroying the message system. + delete gViewerWindow; + gViewerWindow = NULL; + LL_INFOS() << "ViewerWindow deleted" << LL_ENDL; + } + + LLSplashScreen::show(); + LLSplashScreen::update(LLTrans::getString("ShuttingDown")); + + LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL; + + // viewer UI relies on keyboard so keep it aound until viewer UI isa gone + delete gKeyboard; + gKeyboard = NULL; + + if (LLViewerJoystick::instanceExists()) + { + // Turn off Space Navigator and similar devices + LLViewerJoystick::getInstance()->terminate(); + } + LL_INFOS() << "Shutting down message system" << LL_ENDL; end_messaging_system(); @@ -2292,10 +2354,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string) // Callback for LLError::LLUserWarningMsg void errorHandler(const std::string& title_string, const std::string& message_string, S32 code) { - if (!message_string.empty()) - { - OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); - } + // message is going to hang viewer, create marker first switch (code) { case LLError::LLUserWarningMsg::ERROR_OTHER: @@ -2303,6 +2362,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st break; case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC: LLAppViewer::instance()->createErrorMarker(LAST_EXEC_BAD_ALLOC); + // When system run out of memory and errorHandler gets called from a thread, + // main thread might keep going while OSMessageBox freezes the caller. + // Todo: handle it better, but for now disconnect to avoid making things worse + gDisconnected = true; break; case LLError::LLUserWarningMsg::ERROR_MISSING_FILES: LLAppViewer::instance()->createErrorMarker(LAST_EXEC_MISSING_FILES); @@ -2310,6 +2373,25 @@ void errorHandler(const std::string& title_string, const std::string& message_st default: break; } + if (!message_string.empty()) + { + if (on_main_thread()) + { + // Prevent watchdog from killing us while dialog is up. + // Can't do pauseMainloopTimeout, since this may be called + // from threads and we are not going to need watchdog now. + LLAppViewer::instance()->pauseMainloopTimeout(); + + // todo: might want to have non-crashing timeout for OOM cases + // and needs a way to pause main loop. + OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); + LLAppViewer::instance()->resumeMainloopTimeout(); + } + else + { + OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK); + } + } } void LLAppViewer::initLoggingAndGetLastDuration() @@ -2402,7 +2484,6 @@ void LLAppViewer::initLoggingAndGetLastDuration() if (gDirUtilp->fileExists(user_data_path_cef_log)) { std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old"); - LLFile::remove(user_data_path_cef_old, ENOENT); LLFile::rename(user_data_path_cef_log, user_data_path_cef_old); } } @@ -3135,7 +3216,15 @@ bool LLAppViewer::initWindow() .height(gSavedSettings.getU32("WindowHeight")) .min_width(gSavedSettings.getU32("MinWindowWidth")) .min_height(gSavedSettings.getU32("MinWindowHeight")) +#ifdef LL_DARWIN + // Setting it to true causes black screen with no UI displayed. + // Given that it's a DEBUG settings and application goes fullscreen + // on mac simply by expanding it, it was decided to not support/use + // this setting on mac. + .fullscreen(false) +#else // LL_DARWIN .fullscreen(gSavedSettings.getBOOL("FullScreen")) +#endif .ignore_pixel_depth(ignorePixelDepth) .first_run(mIsFirstRun); @@ -3145,7 +3234,7 @@ bool LLAppViewer::initWindow() // Need to load feature table before cheking to start watchdog. bool use_watchdog = false; - int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); + S32 watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); if (watchdog_enabled_setting == -1) { use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); @@ -3164,22 +3253,22 @@ bool LLAppViewer::initWindow() if (use_watchdog) { - LLWatchdog::getInstance()->init(); + LLWatchdog::getInstance()->init([]() + { + LLAppViewer* app = LLAppViewer::instance(); + if (app->logoutRequestSent()) + { + app->createErrorMarker(LAST_EXEC_LOGOUT_FROZE); + } + else + { + app->createErrorMarker(LAST_EXEC_FROZE); + } + }); } LLNotificationsUI::LLNotificationManager::getInstance(); - -#ifdef LL_DARWIN - //Satisfy both MAINT-3135 (OSX 10.6 and earlier) MAINT-3288 (OSX 10.7 and later) - LLOSInfo& os_info = LLOSInfo::instance(); - if (os_info.mMajorVer == 10 && os_info.mMinorVer < 7) - { - if ( os_info.mMinorVer == 6 && os_info.mBuild < 8 ) - gViewerWindow->getWindow()->setOldResize(true); - } -#endif - if (gSavedSettings.getBOOL("WindowMaximized")) { gViewerWindow->getWindow()->maximize(); @@ -3294,6 +3383,11 @@ LLSD LLAppViewer::getViewerInfo() const info["VIEWER_VERSION_STR"] = versionInfo.getVersion(); info["CHANNEL"] = versionInfo.getChannel(); info["ADDRESS_SIZE"] = ADDRESS_SIZE; +#if LL_ARM64 + info["ARCHITECTURE"] = "ARM"; +#else + info["ARCHITECTURE"] = "x86"; +#endif std::string build_config = versionInfo.getBuildConfig(); if (build_config != "Release") { @@ -3375,6 +3469,7 @@ LLSD LLAppViewer::getViewerInfo() const } #endif + info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)"; info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); // Settings @@ -3385,7 +3480,7 @@ LLSD LLAppViewer::getViewerInfo() const info["FONT_SIZE_ADJUSTMENT"] = gSavedSettings.getF32("FontScreenDPI"); info["UI_SCALE"] = gSavedSettings.getF32("UIScaleFactor"); info["DRAW_DISTANCE"] = gSavedSettings.getF32("RenderFarClip"); - info["NET_BANDWITH"] = gSavedSettings.getF32("ThrottleBandwidthKBPS"); + info["NET_BANDWITH"] = LLViewerThrottle::getMaxBandwidthKbps(); info["LOD_FACTOR"] = gSavedSettings.getF32("RenderVolumeLODFactor"); info["RENDER_QUALITY"] = (F32)gSavedSettings.getU32("RenderQualityPerformance"); info["TEXTURE_MEMORY"] = LLSD::Integer(gGLManager.mVRAM); @@ -3420,7 +3515,7 @@ LLSD LLAppViewer::getViewerInfo() const info["VOICE_VERSION"] = LLTrans::getString("NotConnected"); } -#if LL_DARWIN || LL_LINUX || __FreeBSD__ +#if !_M_ARM64 // !LL_LINUX std::ostringstream cef_ver_codec; cef_ver_codec << "Dullahan: "; cef_ver_codec << DULLAHAN_VERSION_MAJOR; @@ -3450,7 +3545,7 @@ LLSD LLAppViewer::getViewerInfo() const info["LIBCEF_VERSION"] = "Undefined"; #endif -//#if !LL_LINUX +#if !_M_ARM64 // !LL_LINUX std::ostringstream vlc_ver_codec; vlc_ver_codec << LIBVLC_VERSION_MAJOR; vlc_ver_codec << "."; @@ -3458,11 +3553,9 @@ LLSD LLAppViewer::getViewerInfo() const vlc_ver_codec << "."; vlc_ver_codec << LIBVLC_VERSION_REVISION; info["LIBVLC_VERSION"] = vlc_ver_codec.str(); -/* #else info["LIBVLC_VERSION"] = "Undefined"; #endif -*/ S32 packets_in = (S32)LLViewerStats::instance().getRecording().getSum(LLStatViewer::PACKETS_IN); if (packets_in > 0) @@ -3627,10 +3720,15 @@ void LLAppViewer::writeSystemInfo() if (! gDebugInfo.has("Dynamic") ) gDebugInfo["Dynamic"] = LLSD::emptyMap(); -#if LL_WINDOWS && !LL_BUGSPLAT +#if LL_DARWIN + // crash processing in CrashMetadataSingleton reads SLLog + gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.crash"); +#elif LL_WINDOWS && !LL_BUGSPLAT gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_DUMP,"SecondLife.log"); #else - //Not ideal but sufficient for good reporting. + // Far from ideal, especially when multiple instances get involved. + // Note that attachmentsForBugSplat expects .old extendion. + // Todo: improve. gDebugInfo["SLLog"] = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old"); //LLError::logFileName(); #endif @@ -3949,8 +4047,15 @@ void LLAppViewer::processMarkerFiles() else if (marker_is_same_version) { // the file existed, is ours, and matched our version, so we can report on what it says - LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed" << LL_ENDL; + LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed or froze" << LL_ENDL; +#if LL_WINDOWS && LL_BUGSPLAT + // bugsplat will set correct state in bugsplatSendLog + // Might be more accurate to rename this one into 'unknown' + gLastExecEvent = LAST_EXEC_UNKNOWN; +#else gLastExecEvent = LAST_EXEC_OTHER_CRASH; +#endif // LL_WINDOWS + } else { @@ -3992,7 +4097,8 @@ void LLAppViewer::processMarkerFiles() { if (markerIsSameVersion(logout_marker_file)) { - gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; + // Either froze, got killed or somehow crash was not caught + gLastExecEvent = LAST_EXEC_LOGOUT_UNKNOWN; LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "', changing LastExecEvent to LOGOUT_FROZE" << LL_ENDL; } else @@ -4030,6 +4136,22 @@ void LLAppViewer::processMarkerFiles() } LLAPRFile::remove(error_marker_file); } + +#if LL_DARWIN + if (!mSecondInstance && gLastExecEvent != LAST_EXEC_NORMAL) + { + // While windows reports crashes immediately, mac reports next run and + // may take a while to trigger crash report so it has a special file. + // Remove .crash file if exists + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.old"); + std::string crash_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.crash"); + LLFile::remove(crash_log_file); + // Rename ".old" log file to ".crash" + LLFile::rename(old_log_file, crash_log_file); + } +#endif } void LLAppViewer::removeMarkerFiles() @@ -4123,6 +4245,7 @@ void LLAppViewer::requestQuit() return; } + pingMainloopTimeout("Main:qMetrics"); // Try to send metrics back to the grid metricsSend(!gDisconnected); @@ -4138,6 +4261,7 @@ void LLAppViewer::requestQuit() LLHUDManager::getInstance()->sendEffects(); effectp->markDead() ;//remove it. + pingMainloopTimeout("Main:qFloaters"); // Attempt to close all floaters that might be // editing things. if (gFloaterView) @@ -4146,6 +4270,7 @@ void LLAppViewer::requestQuit() gFloaterView->closeAllChildren(true); mClosingFloaters = true; } + pingMainloopTimeout("Main:qStats"); // Send preferences once, when exiting bool include_preferences = true; @@ -4153,6 +4278,7 @@ void LLAppViewer::requestQuit() gLogoutTimer.reset(); mQuitRequested = true; + pingMainloopTimeout("Main:LoggingOut"); } static bool finish_quit(const LLSD& notification, const LLSD& response) @@ -4199,7 +4325,7 @@ void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions) // case where we need the viewer to exit without any need for notifications void LLAppViewer::earlyExitNoNotify() { - LL_WARNS() << "app_early_exit with no notification: " << LL_ENDL; + LL_WARNS() << "app_early_exit with no notification." << LL_ENDL; gDoDisconnect = true; finish_early_exit( LLSD(), LLSD() ); } @@ -4297,7 +4423,7 @@ U32 LLAppViewer::getTextureCacheVersion() U32 LLAppViewer::getDiskCacheVersion() { // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. - const U32 DISK_CACHE_VERSION = 1; + const U32 DISK_CACHE_VERSION = 3; return DISK_CACHE_VERSION ; } @@ -4323,8 +4449,8 @@ bool LLAppViewer::initCache() const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); const U32 MB = 1024 * 1024; - const uintmax_t MIN_CACHE_SIZE = 256 * MB; - const uintmax_t MAX_CACHE_SIZE = 9984ll * MB; + const uintmax_t MIN_CACHE_SIZE = 896 * MB; + const uintmax_t MAX_CACHE_SIZE = 32768ll * MB; const uintmax_t setting_cache_total_size = uintmax_t(gSavedSettings.getU32("CacheSize")) * MB; const uintmax_t cache_total_size = llclamp(setting_cache_total_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); const F64 disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); @@ -4384,6 +4510,8 @@ bool LLAppViewer::initCache() LL_WARNS("AppCache") << "Unable to set cache location" << LL_ENDL; gSavedSettings.setString("CacheLocation", ""); gSavedSettings.setString("CacheLocationTopFolder", ""); + gSavedSettings.setString("NewCacheLocation", ""); + gSavedSettings.setString("NewCacheLocationTopFolder", ""); } const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); @@ -4430,10 +4558,13 @@ bool LLAppViewer::initCache() const U32 CACHE_NUMBER_OF_REGIONS_FOR_OBJECTS = 128; LLVOCache::getInstance()->initCache(LL_PATH_CACHE, CACHE_NUMBER_OF_REGIONS_FOR_OBJECTS, getObjectCacheVersion()); + // Remove old, stale CEF cache folders + purgeCefStaleCaches(); + return true; } -void LLAppViewer::addOnIdleCallback(const boost::function<void()>& cb) +void LLAppViewer::addOnIdleCallback(const std::function<void()>& cb) { gMainloopWork.post(cb); } @@ -4454,18 +4585,28 @@ void LLAppViewer::loadKeyBindings() LLUrlRegistry::instance().setKeybindingHandler(&gViewerInput); } +// As per GHI #4498, remove old, stale CEF cache folders from previous sessions +void LLAppViewer::purgeCefStaleCaches() +{ + // TODO: we really shouldn't use a hard coded name for the cache folder here... + const std::string browser_parent_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "cef_cache"); + if (LLFile::isdir(browser_parent_cache)) + { + // This is a sledgehammer approach - nukes the cef_cache dir entirely + // which is then recreated the first time a CEF instance creates an + // individual cache folder. If we ever decide to retain some folders + // e.g. Search UI cache - then we will need a more granular approach. + gDirUtilp->deleteDirAndContents(browser_parent_cache); + } +} + void LLAppViewer::purgeCache() { LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << LL_ENDL; LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE); LLVOCache::getInstance()->removeCache(LL_PATH_CACHE); LLViewerShaderMgr::instance()->clearShaderCache(); - std::string browser_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "cef_cache"); - if (LLFile::isdir(browser_cache)) - { - // cef does not support clear_cache and clear_cookies, so clear what we can manually. - gDirUtilp->deleteDirAndContents(browser_cache); - } + purgeCefStaleCaches(); gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*"); } @@ -4534,6 +4675,7 @@ void LLAppViewer::forceDisconnect(const std::string& mesg) } else { + sendSimpleLogoutRequest(); args["MESSAGE"] = big_reason; LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect ); } @@ -4589,6 +4731,7 @@ void LLAppViewer::saveFinalSnapshot() false, gSavedSettings.getBOOL("RenderHUDInSnapshot"), true, + false, LLSnapshotModel::SNAPSHOT_TYPE_COLOR, LLSnapshotModel::SNAPSHOT_FORMAT_PNG); mSavedFinalSnapshot = true; @@ -4616,11 +4759,32 @@ void LLAppViewer::saveFinalSnapshot() } } +static const char PRODUCTION_CACHE_FORMAT_STRING[] = "%s.%s"; +static const char GRID_CACHE_FORMAT_STRING[] = "%s.%s.%s"; +std::string get_name_cache_filename(const std::string &base_file, const std::string& extention) +{ + std::string filename; + std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, base_file)); + if (LLGridManager::getInstance()->isInProductionGrid()) + { + filename = llformat(PRODUCTION_CACHE_FORMAT_STRING, path.c_str(), extention.c_str()); + } + else + { + // NOTE: The inventory cache filenames now include the grid name. + // Add controls against directory traversal or problematic pathname lengths + // if your viewer uses grid names from an untrusted source. + const std::string& grid_id_str = LLGridManager::getInstance()->getGridId(); + const std::string& grid_id_lower = utf8str_tolower(grid_id_str); + filename = llformat(GRID_CACHE_FORMAT_STRING, path.c_str(), grid_id_lower.c_str(), extention.c_str()); + } + return filename; +} + void LLAppViewer::loadNameCache() { // display names cache - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + std::string filename = get_name_cache_filename("avatar_name_cache", "xml"); LL_INFOS("AvNameCache") << filename << LL_ENDL; llifstream name_cache_stream(filename.c_str()); if(name_cache_stream.is_open()) @@ -4635,8 +4799,8 @@ void LLAppViewer::loadNameCache() if (!gCacheName) return; - std::string name_cache; - name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + // is there a reason for the "cache" extention? + std::string name_cache = get_name_cache_filename("name", "cache"); llifstream cache_file(name_cache.c_str()); if(cache_file.is_open()) { @@ -4647,8 +4811,7 @@ void LLAppViewer::loadNameCache() void LLAppViewer::saveNameCache() { // display names cache - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + std::string filename = get_name_cache_filename("avatar_name_cache", "xml"); llofstream name_cache_stream(filename.c_str()); if(name_cache_stream.is_open()) { @@ -4658,8 +4821,7 @@ void LLAppViewer::saveNameCache() // real names cache if (gCacheName) { - std::string name_cache; - name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + std::string name_cache = get_name_cache_filename("name", "cache"); llofstream cache_file(name_cache.c_str()); if(cache_file.is_open()) { @@ -4937,6 +5099,20 @@ void LLAppViewer::idle() if (gTeleportDisplay) { + if (gAgent.getTeleportState() == LLAgent::TELEPORT_ARRIVING) + { + // Teleported, but waiting for things to load, start processing surface data + { + LL_RECORD_BLOCK_TIME(FTM_NETWORK); + gVLManager.unpackData(); + } + { + LL_RECORD_BLOCK_TIME(FTM_REGION_UPDATE); + const F32 max_region_update_time = .001f; // 1ms + LLWorld::getInstance()->updateRegions(max_region_update_time); + } + } + return; } @@ -5232,15 +5408,28 @@ void LLAppViewer::sendLogoutRequest() gLogoutInProgress = true; if (!mSecondInstance) { - mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME); - - mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB); - if (mLogoutMarkerFile.getFileHandle()) + mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); + try { - LL_INFOS("MarkerFile") << "Created logout marker file '"<< mLogoutMarkerFileName << "' " << LL_ENDL; - recordMarkerVersion(mLogoutMarkerFile); + if (!mLogoutMarkerFile.getFileHandle()) + { + mLogoutMarkerFile.open(mLogoutMarkerFileName, LL_APR_WB); + if (mLogoutMarkerFile.getFileHandle()) + { + LL_INFOS("MarkerFile") << "Created logout marker file '" << mLogoutMarkerFileName << "' " << LL_ENDL; + recordMarkerVersion(mLogoutMarkerFile); + } + else + { + LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL; + } + } + else + { + LL_WARNS("MarkerFile") << "Atempted to reopen file '" << mLogoutMarkerFileName << "' " << LL_ENDL; + } } - else + catch (...) { LL_WARNS("MarkerFile") << "Cannot create logout marker file " << mLogoutMarkerFileName << LL_ENDL; } @@ -5257,6 +5446,8 @@ void LLAppViewer::sendLogoutRequest() msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); gAgent.sendReliableMessage(); + LL_INFOS("Agent") << "Logging out as agent: " << gAgent.getID() << " Session: " << gAgent.getSessionID() << LL_ENDL; + gLogoutTimer.reset(); gLogoutMaxTime = LOGOUT_REQUEST_TIME; mLogoutRequestSent = true; @@ -5265,6 +5456,27 @@ void LLAppViewer::sendLogoutRequest() } } +void LLAppViewer::sendSimpleLogoutRequest() +{ + if (!mLogoutRequestSent && gMessageSystem) + { + gLogoutInProgress = true; + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LogoutRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + + LL_INFOS("Agent") << "Logging out as agent: " << gAgent.getID() << " Session: " << gAgent.getSessionID() << LL_ENDL; + + gLogoutTimer.reset(); + gLogoutMaxTime = LOGOUT_REQUEST_TIME; + mLogoutRequestSent = true; + } +} + void LLAppViewer::updateNameLookupUrl(const LLViewerRegion * regionp) { if (!regionp || !regionp->capabilitiesReceived()) @@ -5329,6 +5541,12 @@ void LLAppViewer::createErrorMarker(eLastExecEvent error_code) const } } +bool LLAppViewer::errorMarkerExists() const +{ + std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); + return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB); +} + void LLAppViewer::outOfMemorySoftQuit() { if (!mQuitRequested) @@ -5341,6 +5559,7 @@ void LLAppViewer::outOfMemorySoftQuit() LLLFSThread::sLocal->pause(); gLogoutTimer.reset(); mQuitRequested = true; + destroyMainloopTimeout(); LLError::LLUserWarningMsg::showOutOfMemory(); } @@ -5373,12 +5592,9 @@ void LLAppViewer::idleNameCache() // Handle messages, and all message related stuff // -#define TIME_THROTTLE_MESSAGES -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +constexpr F32 CHECK_MESSAGES_DEFAULT_MAX_TIME = 0.020f; // 50 ms = 50 fps (just for messages!) static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif static LLTrace::BlockTimerStatHandle FTM_IDLE_NETWORK("Idle Network"); static LLTrace::BlockTimerStatHandle FTM_MESSAGE_ACKS("Message Acks"); @@ -5395,7 +5611,8 @@ void LLAppViewer::idleNetwork() gObjectList.mNumNewObjects = 0; S32 total_decoded = 0; - if (!gSavedSettings.getBOOL("SpeedTest")) + static LLCachedControl<bool> speed_test(gSavedSettings, "SpeedTest", false); + if (!speed_test()) { LL_PROFILE_ZONE_NAMED_CATEGORY_NETWORK("idle network"); //LL_RECORD_BLOCK_TIME(FTM_IDLE_NETWORK); // decode @@ -5405,6 +5622,7 @@ void LLAppViewer::idleNetwork() F32 total_time = 0.0f; { + bool needs_drain = false; LockMessageChecker lmc(gMessageSystem); while (lmc.checkAllMessages(frame_count, gServicePump)) { @@ -5417,60 +5635,56 @@ void LLAppViewer::idleNetwork() } total_decoded++; - gPacketsIn++; if (total_decoded > MESSAGE_MAX_PER_FRAME) { + needs_drain = true; break; } -#ifdef TIME_THROTTLE_MESSAGES // Prevent slow packets from completely destroying the frame rate. // This usually happens due to clumps of avatars taking huge amount // of network processing time (which needs to be fixed, but this is // a good limit anyway). total_time = check_message_timer.getElapsedTimeF32(); if (total_time >= CheckMessagesMaxTime) + { + needs_drain = true; break; -#endif + } + } + if (needs_drain || gMessageSystem->mPacketRing.getNumBufferedPackets() > 0) + { + // Rather than allow packets to silently backup on the socket + // we drain them into our own buffer so we know how many exist. + S32 num_buffered_packets = gMessageSystem->drainUdpSocket(); + if (num_buffered_packets > 0) + { + // Increase CheckMessagesMaxTime so that we will eventually catch up + CheckMessagesMaxTime *= 1.035f; // 3.5% ~= 2x in 20 frames, ~8x in 60 frames + } + } + else + { + // Reset CheckMessagesMaxTime to default value + CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; } // Handle per-frame message system processing. - lmc.processAcks(gSavedSettings.getF32("AckCollectTime")); - } -#ifdef TIME_THROTTLE_MESSAGES - if (total_time >= CheckMessagesMaxTime) - { - // Increase CheckMessagesMaxTime so that we will eventually catch up - CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames - } - else - { - // Reset CheckMessagesMaxTime to default value - CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; - } -#endif - - // Decode enqueued messages... - S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - - if( remaining_possible_decodes <= 0 ) - { - LL_INFOS() << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << LL_ENDL; - } - - if (gPrintMessagesThisFrame) - { - LL_INFOS() << "Decoded " << total_decoded << " msgs this frame!" << LL_ENDL; - gPrintMessagesThisFrame = false; + static LLCachedControl<F32> ack_collection_time(gSavedSettings, "AckCollectTime", 0.1f); + lmc.processAcks(ack_collection_time()); } } add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); // Retransmit unacknowledged packets. - gXferManager->retransmitUnackedPackets(); + if (gXferManager) + { + gXferManager->retransmitUnackedPackets(); + } gAssetStorage->checkForTimeouts(); + gViewerThrottle.setBufferLoadRate(gMessageSystem->getBufferLoadRate()); gViewerThrottle.updateDynamicThrottle(); // Check that the circuit between the viewer and the agent's current @@ -5589,6 +5803,8 @@ void LLAppViewer::forceErrorBreakpoint() DebugBreak(); #elif __i386__ || __x86_64__ asm ("int $3"); +#else + __builtin_trap(); #endif return; } @@ -5645,6 +5861,27 @@ void LLAppViewer::forceErrorCoroutineCrash() LLCoros::instance().launch("LLAppViewer::crashyCoro", [] {throw LLException("A deliberate crash from LLCoros"); }); } +void LLAppViewer::forceErrorCoroprocedureCrash() +{ + LL_WARNS() << "Forcing a crash in LLCoprocedureManager" << LL_ENDL; + LLCoprocedureManager::instance().enqueueCoprocedure("Upload", "DeliberateCrash", + [](LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t&, const LLUUID&) + { + LL_WARNS() << "Forcing a deliberate bad memory access from LLCoprocedureManager" << LL_ENDL; + S32* crash = NULL; + *crash = 0xDEADBEEF; + }); +} + +void LLAppViewer::forceErrorWorkQueueCrash() +{ + LL::WorkQueue::ptr_t workqueue = LL::WorkQueue::getInstance("General"); + if (workqueue) + { + workqueue->post([]() { throw LLException("This is a deliberate crash from General Queue"); }); + } +} + void LLAppViewer::forceErrorThreadCrash() { class LLCrashTestThread : public LLThread @@ -5666,68 +5903,105 @@ void LLAppViewer::forceErrorThreadCrash() thread->start(); } -void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::forceExceptionThreadCrash() +{ + class LLCrashTestThread : public LLThread + { + public: + + LLCrashTestThread() : LLThread("Crash logging test thread") + { + } + + void run() + { + const std::string exception_text = "This is a deliberate exception in a thread"; + throw std::runtime_error(exception_text); + } + }; + + LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL; + LLCrashTestThread* thread = new LLCrashTestThread(); + thread->start(); +} + +void LLAppViewer::initMainloopTimeout(std::string_view state) { - if(!mMainloopTimeout) + if (!mMainloopTimeout) { - mMainloopTimeout = new LLWatchdogTimeout(); - resumeMainloopTimeout(state, secs); + mMainloopTimeout = new LLWatchdogTimeout("mainloop"); + resumeMainloopTimeout(state); } } void LLAppViewer::destroyMainloopTimeout() { - if(mMainloopTimeout) + if (mMainloopTimeout) { delete mMainloopTimeout; - mMainloopTimeout = NULL; + mMainloopTimeout = nullptr; } } -void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::resumeMainloopTimeout(std::string_view state) { - if(mMainloopTimeout) + if (mMainloopTimeout) { - if(secs < 0.0f) - { - static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); - secs = mainloop_timeout; - } - - mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->setTimeout(getMainloopTimeoutSec()); mMainloopTimeout->start(state); } } void LLAppViewer::pauseMainloopTimeout() { - if(mMainloopTimeout) + if (mMainloopTimeout) { mMainloopTimeout->stop(); } } -void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs) +void LLAppViewer::pingMainloopTimeout(std::string_view state) { LL_PROFILE_ZONE_SCOPED_CATEGORY_APP; - if(mMainloopTimeout) + if (mMainloopTimeout) { - if(secs < 0.0f) - { - static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60); - secs = mainloop_timeout; - } - - mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->setTimeout(getMainloopTimeoutSec()); mMainloopTimeout->ping(state); } } + +F32 LLAppViewer::getMainloopTimeoutSec() const +{ + if (isQuitting() || mQuitRequested) + { + constexpr F32 QUITTING_SECONDS = 240.f; + return QUITTING_SECONDS; + } + if (LLStartUp::getStartupState() == STATE_STARTED + && gAgent.getTeleportState() == LLAgent::TELEPORT_NONE) + { + // consider making this value match 'disconnected' timout. + static LLCachedControl<F32> mainloop_started(gSavedSettings, "MainloopTimeoutStarted", 60.f); + return mainloop_started(); + } + else + { + static LLCachedControl<F32> mainloop_default(gSavedSettings, "MainloopTimeoutDefault", 120.f); + return mainloop_default(); + } +} + void LLAppViewer::handleLoginComplete() { gLoggedInTime.start(); initMainloopTimeout("Mainloop Init"); + LLWindow* viewer_window = gViewerWindow->getWindow(); + if (viewer_window) // in case of a headless client + { + viewer_window->initWatchdog(); + } // Store some data to DebugInfo in case of a freeze. gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::instance().getChannel(); @@ -5840,3 +6114,104 @@ void LLAppViewer::metricsSend(bool enable_reporting) // resolution in time. gViewerAssetStats->restart(); } + +#ifdef LL_DISCORD + +void LLAppViewer::initDiscordSocial() +{ + gDiscordPartyCurrentSize = 1; + gDiscordPartyMaxSize = 0; + gDiscordTimestampsStart = time(nullptr); + gDiscordClient = std::make_shared<discordpp::Client>(); + gDiscordClient->SetApplicationId(1393451183741599796); + updateDiscordActivity(); +} + +void LLAppViewer::updateDiscordActivity() +{ + LL_PROFILE_ZONE_SCOPED; + + static LLCachedControl<bool> integration_enabled(gSavedSettings, "EnableDiscord", true); + if (!integration_enabled) + { + gDiscordClient->ClearRichPresence(); + return; + } + + discordpp::Activity activity; + activity.SetType(discordpp::ActivityTypes::Playing); + discordpp::ActivityTimestamps timestamps; + timestamps.SetStart(gDiscordTimestampsStart); + activity.SetTimestamps(timestamps); + + if (gAgent.getID() == LLUUID::null) + { + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); + return; + } + + static LLCachedControl<bool> show_details(gSavedSettings, "ShowDiscordActivityDetails", false); + if (show_details) + { + if (gDiscordActivityDetails.empty()) + { + LLAvatarName av_name; + LLAvatarNameCache::get(gAgent.getID(), &av_name); + gDiscordActivityDetails = av_name.getUserName(); + auto displayName = av_name.getDisplayName(); + if (gDiscordActivityDetails != displayName) + gDiscordActivityDetails = displayName + " (" + gDiscordActivityDetails + ")"; + } + activity.SetDetails(gDiscordActivityDetails); + } + + auto agent_pos_region = gAgent.getPositionAgent(); + S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); + S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); + S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); + F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); + const F32 FLY_CUTOFF = 6.f; + const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; + const F32 WALK_CUTOFF = 1.5f; + const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; + if (velocity_mag_sq > FLY_CUTOFF_SQ) + { + pos_x -= pos_x % 4; + pos_y -= pos_y % 4; + } + else if (velocity_mag_sq > WALK_CUTOFF_SQ) + { + pos_x -= pos_x % 2; + pos_y -= pos_y % 2; + } + + std::string location = "Hidden Region"; + static LLCachedControl<bool> show_state(gSavedSettings, "ShowDiscordActivityState", false); + if (show_state) + { + location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z); + } + activity.SetState(location); + + discordpp::ActivityParty party; + party.SetId(location); + party.SetCurrentSize(gDiscordPartyCurrentSize); + party.SetMaxSize(gDiscordPartyMaxSize); + activity.SetParty(party); + + gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {}); +} + +void LLAppViewer::updateDiscordPartyCurrentSize(int32_t size) +{ + gDiscordPartyCurrentSize = size; + updateDiscordActivity(); +} + +void LLAppViewer::updateDiscordPartyMaxSize(int32_t size) +{ + gDiscordPartyMaxSize = size; + updateDiscordActivity(); +} + +#endif |
