Skip to content
Snippets Groups Projects
llappviewer.cpp 172 KiB
Newer Older
	LL_INFOS("InitInfo") << "Window is initialized." << LL_ENDL ;
	// initWindow also initializes the Feature List, so now we can initialize this global.
	LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap");

Kent Quirk's avatar
Kent Quirk committed
	// call all self-registered classes
	LLInitClassList::instance().fireCallbacks();

	LLFolderViewItem::initClass(); // SJB: Needs to happen after initWindow(), not sure why but related to fonts
	gGLManager.printGLInfoString();
	std::string key_bindings_file = gDirUtilp->findFile("keys.xml",
														gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""),
														gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));


	if (!gViewerKeyboard.loadBindingsXML(key_bindings_file))
		std::string key_bindings_file = gDirUtilp->findFile("keys.ini",
															gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""),
															gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
		if (!gViewerKeyboard.loadBindings(key_bindings_file))
		{
			LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL;
		}
	}

	// If we don't have the right GL requirements, exit.
	if (!gGLManager.mHasRequirements)
Kent Quirk's avatar
Kent Quirk committed
		// can't use an alert here since we're exiting and
		LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedGLRequirements");
			details.getString(),
    // If we don't have the right shader requirements.
    if (!gGLManager.mHasShaderObjects
        || !gGLManager.mHasVertexShader
        || !gGLManager.mHasFragmentShader)
    {
        LLUIString details = LLNotifications::instance().getGlobalString("UnsupportedShaderRequirements");
        OSMessageBox(
            details.getString(),
            LLStringUtil::null,
            OSMB_OK);
        return 0;
    }

	// Without SSE2 support we will crash almost immediately, warn here.
	if (!gSysCPU.hasSSE2())
		// can't use an alert here since we're exiting and
		// all hell breaks lose.
		OSMessageBox(
			LLNotifications::instance().getGlobalString("UnsupportedCPUSSE2"),
			LLStringUtil::null,
			OSMB_OK);
		return 0;
	}

Kent Quirk's avatar
Kent Quirk committed
	// alert the user if they are using unsupported hardware
	if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware"))
Kent Quirk's avatar
Kent Quirk committed
		bool unsupported = false;
		LLSD args;
		std::string minSpecs;
Kent Quirk's avatar
Kent Quirk committed
		// get cpu data from xml
		std::stringstream minCPUString(LLNotifications::instance().getGlobalString("UnsupportedCPUAmount"));
		S32 minCPU = 0;
		minCPUString >> minCPU;

		// get RAM data from XML
		std::stringstream minRAMString(LLNotifications::instance().getGlobalString("UnsupportedRAMAmount"));
Kent Quirk's avatar
Kent Quirk committed
		minRAMString >> minRAM;

		if(!LLFeatureManager::getInstance()->isGPUSupported() && LLFeatureManager::getInstance()->getGPUClass() != GPU_CLASS_UNKNOWN)
		{
			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedGPU");
			minSpecs += "\n";
			unsupported = true;
		}
Kent Quirk's avatar
Kent Quirk committed
		{
			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedCPU");
			minSpecs += "\n";
			unsupported = true;
		}
		if(gSysMemory.getPhysicalMemoryKB() < minRAM)
Kent Quirk's avatar
Kent Quirk committed
			minSpecs += LLNotifications::instance().getGlobalString("UnsupportedRAM");
			minSpecs += "\n";
			unsupported = true;
Kent Quirk's avatar
Kent Quirk committed
		if (LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_UNKNOWN)
		{
			LLNotificationsUtil::add("UnknownGPU");
Kent Quirk's avatar
Kent Quirk committed
		if(unsupported)
		{
			if(!gSavedSettings.controlExists("WarnUnsupportedHardware")
Kent Quirk's avatar
Kent Quirk committed
				|| gSavedSettings.getBOOL("WarnUnsupportedHardware"))
			{
				args["MINSPECS"] = minSpecs;
				LLNotificationsUtil::add("UnsupportedHardware", args );
	if (gGLManager.mGLVersion < LLFeatureManager::getInstance()->getExpectedGLVersion())
			url = LLTrans::getString("IntelDriverPage");
			url = LLTrans::getString("NvidiaDriverPage");
			url = LLTrans::getString("AMDDriverPage");
		}

		if (!url.empty())
		{
			LLNotificationsUtil::add("OldGPUDriver", LLSD().with("URL", url));
	// save the graphics card
	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();

	// Save the current version to the prefs file
	gSavedSettings.setString("LastRunVersion",
							 LLVersionInfo::instance().getChannelAndVersion());

	gSimLastTime = gRenderStartTime.getElapsedTimeF32();
	gSimFrames = (F32)gFrameCount;

    if (gSavedSettings.getBOOL("JoystickEnabled"))
    {
        LLViewerJoystick::getInstance()->init(false);
    }
Rye Mutt's avatar
Rye Mutt committed
	catch (const LLProtectedDataException&)
	{
	  LLNotificationsUtil::add("CorruptedProtectedDataStore");
	}

	gGLActive = FALSE;
#if LL_RELEASE_FOR_DOWNLOAD 
#ifndef LL_LINUX
    if (!gSavedSettings.getBOOL("CmdLineSkipUpdater"))
    {
	LLProcess::Params updater;
	updater.desc = "updater process";
	// Because it's the updater, it MUST persist beyond the lifespan of the
	// viewer itself.
	updater.autokill = false;
	updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker.exe");
	// explicitly run the system Python interpreter on SLVersionChecker.py
	updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "SLVersionChecker.py"));
	updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "SLVersionChecker");
	// add LEAP mode command-line argument to whichever of these we selected
	updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
	updater.args.add(LLVersionInfo::instance().getChannel());
	updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
	updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize")));
		// Run the updater. An exception from launching the updater should bother us.
		LLLeap::create(updater, true);
	}
	else
		LL_WARNS("InitInfo") << "Skipping updater check." << LL_ENDL;
#endif
	// Iterate over --leap command-line options. But this is a bit tricky: if
	// there's only one, it won't be an array at all.
	LLSD LeapCommand(gSavedSettings.getLLSD("LeapCommand"));
	LL_DEBUGS("InitInfo") << "LeapCommand: " << LeapCommand << LL_ENDL;
	if (LeapCommand.isDefined() && ! LeapCommand.isArray())
		// If LeapCommand is actually a scalar value, make an array of it.
		// Have to do it in two steps because LeapCommand.append(LeapCommand)
		// trashes content! :-P
		LLSD item(LeapCommand);
		LeapCommand.append(item);
	}
Rye Mutt's avatar
Rye Mutt committed
	for (const LLSD& leap : llsd::inArray(LeapCommand))
	{
		LL_INFOS("InitInfo") << "processing --leap \"" << leap << '"' << LL_ENDL;
		// We don't have any better description of this plugin than the
		// user-specified command line. Passing "" causes LLLeap to derive a
		// description from the command line itself.
		// Suppress LLLeap::Error exception: trust LLLeap's own logging. We
		// don't consider any one --leap command mission-critical, so if one
		// fails, log it, shrug and carry on.
Rye Mutt's avatar
Rye Mutt committed
		LLLeap::create("", leap.asString(), false); // exception=false
	if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)
	{
		LL_WARNS("InitInfo") << "QAModeEventHostPort DEPRECATED: "
							 << "lleventhost no longer supported as a dynamic library"
							 << LL_ENDL;
#endif
	LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match;
	//EXT-7013 - On windows for some locale (Japanese) standard
	//datetime formatting functions didn't support some parameters such as "weekday".
	//Names for days and months localized in xml are also useful for Polish locale(STORM-107).
	std::string language = gSavedSettings.getString("Language");
	if(language == "ja" || language == "pl")
	{
		LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames"));
		LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames"));
		LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames"));
		LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames"));
		LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat"));

		LLStringOps::sAM = LLTrans::getString("dateTimeAM");
		LLStringOps::sPM = LLTrans::getString("dateTimePM");
	}
    /// 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));

	// 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();

	// Create IO Pump to use for HTTP Requests.
	gServicePump = new LLPumpIO(gAPRPoolp);

	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.

	LLVoiceChannel::initClass();
	LLVoiceClient::initParamSingleton(gServicePump);
	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);

	joystick = LLViewerJoystick::getInstance();
	joystick->setNeedsReset(true);
void LLAppViewer::initMaxHeapSize()
{
	//set the max heap size.
	//here is some info regarding to the max heap size:
	//------------------------------------------------------------------------------------------
	// OS       | setting | SL address bits | max manageable memory space | max heap size
	// Win 32   | default | 32-bit          | 2GB                         | < 1.7GB
	// Win 32   | /3G     | 32-bit          | 3GB                         | < 1.7GB or 2.7GB
	//Linux 32  | default | 32-bit          | 3GB                         | < 2.7GB
	//Linux 32  |HUGEMEM  | 32-bit          | 4GB                         | < 3.7GB
	//64-bit OS |default  | 32-bit          | 4GB                         | < 3.7GB
	//64-bit OS |default  | 64-bit          | N/A (> 4GB)                 | N/A (> 4GB)
	//------------------------------------------------------------------------------------------
	//currently SL is built under 32-bit setting, we set its max heap size no more than 1.6 GB.

	//F32 max_heap_size_gb = llmin(1.6f, (F32)gSavedSettings.getF32("MaxHeapSize")) ;
	F32Gigabytes max_heap_size_gb = (F32Gigabytes)gSavedSettings.getF32("MaxHeapSize") ;
Xiaohong Bao's avatar
Xiaohong Bao committed
	BOOL enable_mem_failure_prevention = (BOOL)gSavedSettings.getBOOL("MemoryFailurePreventionEnabled") ;

	LLMemory::initMaxHeapSizeGB(max_heap_size_gb, enable_mem_failure_prevention) ;
}
void LLAppViewer::checkMemory()
{
	const static F32 MEMORY_CHECK_INTERVAL = 1.0f ; //second
	//const static F32 MAX_QUIT_WAIT_TIME = 30.0f ; //seconds
	//static F32 force_quit_timer = MAX_QUIT_WAIT_TIME + MEMORY_CHECK_INTERVAL ;

	if(MEMORY_CHECK_INTERVAL > mMemCheckTimer.getElapsedTimeF32())
Richard Linden's avatar
Richard Linden committed
		//update the availability of memory
		LLMemory::updateMemoryInfo() ;

	bool is_low = LLMemory::isMemoryPoolLow() ;
	LLPipeline::throttleNewMemoryAllocation(is_low) ;

	if(is_low)
	{
		LLMemory::logMemoryInfo() ;
	}
static LLTrace::BlockTimerStatHandle FTM_MESSAGES("System Messages");
static LLTrace::BlockTimerStatHandle FTM_SLEEP("Sleep");
static LLTrace::BlockTimerStatHandle FTM_YIELD("Yield");

static LLTrace::BlockTimerStatHandle FTM_TEXTURE_CACHE("Texture Cache");
static LLTrace::BlockTimerStatHandle FTM_DECODE("Image Decode");
static LLTrace::BlockTimerStatHandle FTM_FETCH("Image Fetch");

static LLTrace::BlockTimerStatHandle FTM_VFS("VFS Thread");
static LLTrace::BlockTimerStatHandle FTM_LFS("LFS Thread");
static LLTrace::BlockTimerStatHandle FTM_PAUSE_THREADS("Pause Threads");
static LLTrace::BlockTimerStatHandle FTM_IDLE("Idle");
static LLTrace::BlockTimerStatHandle FTM_PUMP("Pump");
static LLTrace::BlockTimerStatHandle FTM_PUMP_SERVICE("Service");
static LLTrace::BlockTimerStatHandle FTM_SERVICE_CALLBACK("Callback");
static LLTrace::BlockTimerStatHandle FTM_AGENT_AUTOPILOT("Autopilot");
static LLTrace::BlockTimerStatHandle FTM_AGENT_UPDATE("Update");

LLTrace::BlockTimerStatHandle FTM_FRAME("Frame");
		catch (const LLContinueError&)
		{
			LOG_UNHANDLED_EXCEPTION("");
		}
Rye Mutt's avatar
Rye Mutt committed
		catch (const std::bad_alloc&)
		{
			LLMemory::logMemoryInfo(TRUE);
			LLFloaterMemLeak* mem_leak_instance = LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
			if (mem_leak_instance)
			{
				mem_leak_instance->stop();
			}
			LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL;
		}
	}
	else
	{ 
		try
		{
			ret = doFrame();
		}
		catch (const LLContinueError&)
		{
			LOG_UNHANDLED_EXCEPTION("");
		}
	LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
Rye Mutt's avatar
Rye Mutt committed
	LL_ALWAYS_RECORD_BLOCK_TIME(FTM_FRAME);
	LLTrace::BlockTimer::processTimes();
	LLTrace::get_frame_recording().nextPeriod();
	LLTrace::BlockTimer::logStats();
	LLTrace::get_thread_recorder()->pullFromChildren();
	//clear call stack records
	LL_CLEAR_CALLSTACKS();
	//check memory availability information
	checkMemory() ;

		pingMainloopTimeout("Main:MiscNativeWindowEvents");
			LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
			gViewerWindow->getWindow()->processMiscNativeEvents();
		}
		pingMainloopTimeout("Main:GatherInput");
		if (gViewerWindow)
		{
			LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
			if (!restoreErrorTrap())
				LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
			gViewerWindow->getWindow()->gatherInput();
		}
			LLFloaterMemLeak* mem_leak_instance =
				LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
			if (mem_leak_instance)
			{
				mem_leak_instance->idle();
			}
		}
		// canonical per-frame event
		mainloop.post(newFrame);
		// give listeners a chance to run
		llcoro::suspend();
		if (!LLApp::isExiting())
		{
			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())
				joystick->scanJoystick();
				gKeyboard->scanKeyboard();
			// Update state based on messages, user input, object idle.
				pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
Rye Mutt's avatar
Rye Mutt committed
				LL_ALWAYS_RECORD_BLOCK_TIME(FTM_IDLE);
			if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
				pauseMainloopTimeout();
				saveFinalSnapshot();
				disconnectViewer();
				resumeMainloopTimeout();
			// Render scene.
			// *TODO: Should we run display() even during gHeadlessClient?  DK 2011-02-18
			if (!LLApp::isExiting() && !gHeadlessClient && gViewerWindow)
				pingMainloopTimeout("Main:Display");
				gGLActive = TRUE;
Graham Linden's avatar
Graham Linden committed
				display();

				pingMainloopTimeout("Main:Snapshot");
				LLFloaterSnapshot::update(); // take snapshots
					LLFloaterOutfitSnapshot::update();
		pingMainloopTimeout("Main:Sleep");
Rye Mutt's avatar
Rye Mutt committed
			LL_ALWAYS_RECORD_BLOCK_TIME(FTM_SLEEP);
			// yield some time to the os based on command line option
			static LLCachedControl<S32> yield_time(gSavedSettings, "YieldTime", -1);
			if(yield_time >= 0)
			// yield cooperatively when not running as foreground window
			// and when not quiting (causes trouble at mac's cleanup stage)
			if (!LLApp::isExiting()
				&& ((gViewerWindow && !gViewerWindow->getWindow()->getVisible())
					|| !gFocusMgr.getAppHasFocus()))
				// Sleep if we're not rendering, or the window is minimized.
				static LLCachedControl<S32> s_bacground_yeild_time(gSavedSettings, "BackgroundYieldTime", 40);
				S32 milliseconds_to_sleep = llclamp((S32)s_bacground_yeild_time, 0, 1000);
				// don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
				// of equal priority on Windows
				if (milliseconds_to_sleep > 0)
					ms_sleep(milliseconds_to_sleep);
					// also pause worker threads during this wait period
					LLAppViewer::getTextureCache()->pause();
					LLAppViewer::getImageDecodeThread()->pause();
				ms_sleep(ll_rand() % 200);
			if (mPeriodicSlowFrame
				&& (gFrameCount % 10 == 0))
				LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
				ms_sleep(500);
			}

			S32 total_work_pending = 0;
			{
				S32 work_pending = 0;
				S32 io_pending = 0;
				F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);

				work_pending += updateTextureThreads(max_time);
					LL_RECORD_BLOCK_TIME(FTM_VFS);
 					io_pending += LLVFSThread::updateClass(1);
					LL_RECORD_BLOCK_TIME(FTM_LFS);
 					io_pending += LLLFSThread::updateClass(1);
					ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
				total_work_pending += work_pending ;
				total_io_pending += io_pending ;
			if(!total_work_pending) //pause texture fetching threads if nothing to process.
			{
				LLAppViewer::getTextureCache()->pause();
				LLAppViewer::getImageDecodeThread()->pause();
				LLAppViewer::getTextureFetch()->pause();
			}
			if(!total_io_pending) //pause file threads if nothing to process.
			{
				LLVFSThread::sLocal->pause();
				LLLFSThread::sLocal->pause();
			}
			//texture fetching debugger
			if(LLTextureFetchDebugger::isEnabled())
			{
				LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
					LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
				if(tex_fetch_debugger_instance)
					tex_fetch_debugger_instance->idle() ;
			resumeMainloopTimeout();

			pingMainloopTimeout("Main:End");
	if (LLApp::isExiting())
		// Save snapshot for next time, if we made it through initialization
		if (STATE_STARTED == LLStartUp::getStartupState())
		if (LLVoiceClient::instanceExists())
		{
			LLVoiceClient::getInstance()->terminate();
		}

		delete gServicePump;
		destroyMainloopTimeout();
		LL_INFOS() << "Exiting main_loop" << LL_ENDL;
S32 LLAppViewer::updateTextureThreads(F32 max_time)
{
	S32 work_pending = 0;
	{
		LL_RECORD_BLOCK_TIME(FTM_TEXTURE_CACHE);
 		work_pending += LLAppViewer::getTextureCache()->update(max_time); // unpauses the texture cache thread
	}
	{
		LL_RECORD_BLOCK_TIME(FTM_DECODE);
	 	work_pending += LLAppViewer::getImageDecodeThread()->update(max_time); // unpauses the image thread
	}
	{
		LL_RECORD_BLOCK_TIME(FTM_FETCH);
	 	work_pending += LLAppViewer::getTextureFetch()->update(max_time); // unpauses the texture fetch thread
	}
	return work_pending;
}

void LLAppViewer::flushVFSIO()
{
	while (1)
	{
		S32 pending = LLVFSThread::updateClass(0);
		pending += LLLFSThread::updateClass(0);
		if (!pending)
		{
			break;
		}
		LL_INFOS() << "Waiting for pending IO to finish: " << pending << LL_ENDL;
    LLAtmosphere::cleanupClass();

	//ditch LLVOAvatarSelf instance
	gAgentAvatarp = NULL;

	// workaround for DEV-35406 crash on shutdown
	LLEventPumps::instance().reset();

	if (LLSceneMonitor::instanceExists())
	{
		LLSceneMonitor::instance().dumpToFile(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "scene_monitor_results.csv"));
	}
	// There used to be an 'if (LLFastTimerView::sAnalyzePerformance)' block
	// here, completely redundant with the one that occurs later in this same
	// function. Presumably the duplication was due to an automated merge gone
	// bad. Not knowing which instance to prefer, we chose to retain the later
	// one because it happens just after mFastTimerLogThread is deleted. This
	// comment is in case we guessed wrong, so we can move it here instead.
	// remove any old breakpad minidump files from the log directory
	if (! isError())
	{
		std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
		gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp");
	}

	// Kill off LLLeap objects. We can find them all because LLLeap is derived
	// from LLInstanceTracker.
	LLLeap::instance_snapshot().deleteAll();
	//flag all elements as needing to be destroyed immediately
	// to ensure shutdown order
	LLMortician::setZealous(TRUE);

    // Give any remaining SLPlugin instances a chance to exit cleanly.
    LLPluginProcessParent::shutdown();

	LL_INFOS() << "Viewer disconnected" << LL_ENDL;
	LLError::logToFixedBuffer(NULL); // stop the fixed buffer recorder
	LL_INFOS() << "Cleaning Up" << LL_ENDL;
	// shut down mesh streamer
	gMeshRepo.shutdown();

	// shut down Havok
	LLPhysicsExtensions::quitSystem();

	// Must clean up texture references before viewer window is destroyed.
	if(LLHUDManager::instanceExists())
	{
		LLHUDManager::getInstance()->updateEffects();
		LLHUDObject::updateAll();
		LLHUDManager::getInstance()->cleanupEffects();
		LLHUDObject::cleanupHUDObjects();
		LL_INFOS() << "HUD Objects cleaned up" << LL_ENDL;
	LLKeyframeDataCache::clear();
 	// End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage)
#if 0 // this seems to get us stuck in an infinite loop...
	gTransferManager.cleanup();
#endif
	// Note: this is where gWorldMap used to be deleted.
	// Note: this is where gHUDManager used to be deleted.
	if(LLHUDManager::instanceExists())
	{
		LLHUDManager::getInstance()->shutdownClass();
	}

	delete gAssetStorage;
	gAssetStorage = NULL;

	LLPolyMesh::freeAllMeshes();

	LLStartUp::cleanupNameCache();
	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted.
	if (LLWorldMap::instanceExists())
	{
		LLWorldMap::getInstance()->reset(); // release any images
	}
	LL_INFOS() << "Global stuff deleted" << LL_ENDL;
Richard Linden's avatar
Richard Linden committed
        gAudiop->shutdown();
	// Note: this is where LLFeatureManager::getInstance()-> used to be deleted.

	// Patch up settings for next time
	// Must do this before we delete the viewer window,
	// such that we can suck rectangle information out of
	// it.
	cleanupSavedSettings();
	LL_INFOS() << "Settings patched up" << LL_ENDL;

	// delete some of the files left around in the cache.
	removeCacheFiles("*.wav");
	removeCacheFiles("*.tmp");
	removeCacheFiles("*.lso");
	removeCacheFiles("*.out");
	removeCacheFiles("*.dsf");
	removeCacheFiles("*.bodypart");
	removeCacheFiles("*.clothing");

	LL_INFOS() << "Cache files removed" << LL_ENDL;
	LL_INFOS() << "Shutting down Views" << LL_ENDL;
	if( gViewerWindow)
		gViewerWindow->shutdownViews();
	LL_INFOS() << "Cleaning up Inventory" << LL_ENDL;
	// Cleanup Inventory after the UI since it will delete any remaining observers
	// (Deleted observers should have already removed themselves)
	gInventory.cleanupInventory();
	LLCoros::getInstance()->printActiveCoroutines();

	LL_INFOS() << "Cleaning up Selections" << LL_ENDL;
	// Clean up selection managers after UI is destroyed, as UI may be observing them.
	// 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;
	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;
	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;
	SUBSYSTEM_CLEANUP(LLAvatarAppearance);
	SUBSYSTEM_CLEANUP(LLAvatarAppearance);
	SUBSYSTEM_CLEANUP(LLPostProcess);
	// *FIX: This is handled in LLAppViewerWin32::cleanup().
	// I'm keeping the comment to remember its order in cleanup,
	// in case of unforseen dependency.
Josh Bell's avatar
Josh Bell committed
	//#if LL_WINDOWS
	//	gDXHardware.cleanup();
	//#endif // LL_WINDOWS
	LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager();
	if (!volume_manager->cleanup())
		LL_WARNS() << "Remaining references in the volume manager!" << LL_ENDL;
	LL_INFOS() << "Additional Cleanup..." << LL_ENDL;

	// *Note: this is where gViewerStats used to be deleted.
	SUBSYSTEM_CLEANUP(LLWorldMapView);
	SUBSYSTEM_CLEANUP(LLFolderViewItem);
	//
	// Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles).
	// Also after viewerwindow is deleted, since it may have image pointers (which have vfiles)
	// Also after shutting down the messaging system since it has VFS dependencies
	LL_INFOS() << "Cleaning up VFS" << LL_ENDL;
	SUBSYSTEM_CLEANUP(LLVFile);
	LL_INFOS() << "Saving Data" << LL_ENDL;
	// Store the time of our current logoff
	gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());

    if (LLEnvironment::instanceExists())
    {
        //Store environment settings if nessesary
        LLEnvironment::getInstance()->saveToSettings();
    }

	// Must do this after all panels have been deleted because panels that have persistent rects
	// save their rects on delete.
	gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);
	LLUIColorTable::instance().saveUserSettings(gSavedSettings.getBool("ResetUserColorsOnLogout"));
Tofu Linden's avatar
Tofu Linden committed
	// PerAccountSettingsFile should be empty if no user has been logged on.
	// *FIX:Mani This should get really saved in a "logoff" mode.
	if (gSavedSettings.getString("PerAccountSettingsFile").empty())
	{
		LL_INFOS() << "Not saving per-account settings; don't know the account name yet." << LL_ENDL;
	// Only save per account settings if the previous login succeeded, otherwise
	// we might end up with a cleared out settings file in case a previous login
	// failed after loading per account settings.
	else if (!mSavePerAccountSettings)
	{
		LL_INFOS() << "Not saving per-account settings; last login was not successful." << LL_ENDL;
	else
	{
		gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE);
		LL_INFOS() << "Saved settings" << LL_ENDL;

		if (LLViewerParcelAskPlay::instanceExists())
		{
			LLViewerParcelAskPlay::getInstance()->saveSettings();
		}
	std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings"));
	gWarningSettings.saveToFile(warnings_settings_filename, TRUE);

Josh Bell's avatar
Josh Bell committed
	// Save URL history file
	LLURLHistory::saveFile("url_history.xml");

	// save mute list. gMuteList used to also be deleted here too.
	if (gAgent.isInitialized() && LLMuteList::instanceExists())
	{
		LLMuteList::getInstance()->cache(gAgent.getID());
	}
	//save call log list
	if (LLConversationLog::instanceExists())
	{
		LLConversationLog::instance().cache();
	}
	if (mPurgeCacheOnExit)
		LL_INFOS() << "Purging all cache files on exit" << LL_ENDL;
		gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""), "*.*");
	writeDebugInfo();
	LLLocationHistory::getInstance()->save();

	LLAvatarIconIDCache::getInstance()->save();
	// Stop the plugin read thread if it's running.
	LLPluginProcessParent::setUseReadThread(false);

	LL_INFOS() << "Shutting down Threads" << LL_ENDL;
	// Let threads finish
	LLTimer idleTimer;
	idleTimer.reset();
	const F64 max_idle_time = 5.f; // 5 seconds
	while(1)
	{
		S32 pending = 0;
		pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread
		pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread
		pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread
		pending += LLVFSThread::updateClass(0);
		pending += LLLFSThread::updateClass(0);
		F64 idle_time = idleTimer.getElapsedTimeF64();