private void statsHeartBeat(object sender, EventArgs e) { if (!m_scene.Active) { return; } // dont do it if if still been done if (Monitor.TryEnter(m_statsLock)) { // m_log.Debug("Firing Stats Heart Beat"); float[] newvalues = new float[(int)StatsIndex.ArraySize]; uint regionFlags = 0; try { if (estateModule == null) { estateModule = m_scene.RequestModuleInterface <IEstateModule>(); } regionFlags = estateModule != null?estateModule.GetRegionFlags() : (uint)0; } catch (Exception) { // leave region flags at 0 } #region various statistic googly moogly double timeTmp = m_lastUpdateTS; m_lastUpdateTS = Util.GetTimeStampMS(); float updateElapsed = (float)((m_lastUpdateTS - timeTmp) / 1000.0); // factor to consider updates integration time float updateTimeFactor = 1.0f / updateElapsed; // scene frame stats float reportedFPS; float physfps; float timeDilation; float agentMS; float physicsMS; float otherMS; float sleeptime; float scriptTimeMS; float totalFrameTime; float invFrameElapsed; // get a copy under lock and reset lock (m_statsFrameLock) { timeDilation = m_timeDilation; reportedFPS = m_fps; physfps = m_pfps; agentMS = m_agentMS; physicsMS = m_physicsMS; otherMS = m_otherMS; sleeptime = m_sleeptimeMS; scriptTimeMS = m_scriptTimeMS; totalFrameTime = m_frameMS; // still not inv invFrameElapsed = (float)((m_FrameStatsTS - m_prevFrameStatsTS) / 1000.0); ResetFrameStats(); } if (invFrameElapsed / updateElapsed < 0.8) { // scene is in trouble, its account of time is most likely wrong // can even be in stall invFrameElapsed = updateTimeFactor; } else { invFrameElapsed = 1.0f / invFrameElapsed; } float perframefactor; if (reportedFPS <= 0) { reportedFPS = 0.0f; physfps = 0.0f; perframefactor = 1.0f; timeDilation = 0.0f; } else { timeDilation /= reportedFPS; reportedFPS *= m_statisticsFPSfactor; perframefactor = 1.0f / (float)reportedFPS; reportedFPS *= invFrameElapsed; physfps *= invFrameElapsed * m_statisticsFPSfactor; } // some engines track frame time with error related to the simulation step size if (physfps > reportedFPS) { physfps = reportedFPS; } // save the reported value so there is something available for llGetRegionFPS lastReportedSimFPS = reportedFPS; // scale frame stats totalFrameTime *= perframefactor; sleeptime *= perframefactor; otherMS *= perframefactor; physicsMS *= perframefactor; agentMS *= perframefactor; scriptTimeMS *= perframefactor; // estimate spare time float sparetime; sparetime = m_targetFrameTime - (physicsMS + agentMS + otherMS); if (sparetime < 0) { sparetime = 0; } else if (sparetime > totalFrameTime) { sparetime = totalFrameTime; } #endregion SceneGraph SG = m_scene.SceneGraph; OnStatsIncorrect?.Invoke(); // number of agents may still drift so fix m_activeScripts = SG.GetActiveScriptsCount(); m_scriptLinesPerSecond = SG.GetScriptLPS(); newvalues[(int)StatsIndex.TimeDilation] = (Single.IsNaN(timeDilation)) ? 0.0f : (float)Math.Round(timeDilation, 3); newvalues[(int)StatsIndex.SimFPS] = (float)Math.Round(reportedFPS, 1); newvalues[(int)StatsIndex.PhysicsFPS] = (float)Math.Round(physfps, 1); newvalues[(int)StatsIndex.AgentUpdates] = m_agentUpdates * updateTimeFactor; newvalues[(int)StatsIndex.Agents] = SG.GetRootAgentCount(); newvalues[(int)StatsIndex.ChildAgents] = SG.GetChildAgentCount(); newvalues[(int)StatsIndex.TotalPrim] = SG.GetTotalPrimObjectsCount(); newvalues[(int)StatsIndex.ActivePrim] = SG.GetActiveObjectsCount(); newvalues[(int)StatsIndex.FrameMS] = totalFrameTime; newvalues[(int)StatsIndex.NetMS] = (float)Math.Round(m_netMS * perframefactor, 3); newvalues[(int)StatsIndex.PhysicsMS] = (float)Math.Round(physicsMS, 3); newvalues[(int)StatsIndex.ImageMS] = (float)Math.Round(m_imageMS * perframefactor, 3); newvalues[(int)StatsIndex.OtherMS] = (float)Math.Round(otherMS, 3); newvalues[(int)StatsIndex.InPacketsPerSecond] = (float)Math.Round(m_inPacketsPerSecond * updateTimeFactor); newvalues[(int)StatsIndex.OutPacketsPerSecond] = (float)Math.Round(m_outPacketsPerSecond * updateTimeFactor); newvalues[(int)StatsIndex.UnAckedBytes] = m_unAckedBytes; newvalues[(int)StatsIndex.AgentMS] = agentMS; newvalues[(int)StatsIndex.PendingDownloads] = m_pendingDownloads; newvalues[(int)StatsIndex.PendingUploads] = m_pendingUploads; newvalues[(int)StatsIndex.ActiveScripts] = m_activeScripts; newvalues[(int)StatsIndex.SimSleepMs] = (float)Math.Round(sleeptime, 3); newvalues[(int)StatsIndex.SimSpareMs] = (float)Math.Round(sparetime, 3); newvalues[(int)StatsIndex.SimPhysicsStepMs] = 20; // this should came from phys engine newvalues[(int)StatsIndex.ScriptMS] = scriptTimeMS; newvalues[(int)StatsIndex.ScriptEps] = (float)Math.Round(m_scriptEventsPerSecond * updateTimeFactor); // add extra stats for internal use newvalues[(int)StatsIndex.LSLScriptLinesPerSecond] = (float)Math.Round(m_scriptLinesPerSecond * updateTimeFactor, 3); newvalues[(int)StatsIndex.FrameDilation2] = (Single.IsNaN(timeDilation)) ? 0.1f : (float)Math.Round(timeDilation, 1); newvalues[(int)StatsIndex.UsersLoggingIn] = m_usersLoggingIn; newvalues[(int)StatsIndex.TotalGeoPrim] = SG.GetTotalPrimObjectsCount(); newvalues[(int)StatsIndex.TotalMesh] = SG.GetTotalMeshObjectsCount(); newvalues[(int)StatsIndex.ScriptEngineThreadCount] = m_inUseThreads; newvalues[(int)StatsIndex.NPCs] = SG.GetRootNPCCount(); lastReportedSimStats = newvalues; OnSendStatsResult?.Invoke(new SimStats( ReportingRegion.RegionLocX, ReportingRegion.RegionLocY, ReportingRegion.RegionSizeX, ReportingRegion.RegionSizeY, regionFlags, (uint)m_objectCapacity, newvalues, m_scene.RegionInfo.originRegionID, m_scene.RegionInfo.RegionName) ); // Extra statistics that aren't currently sent elsewhere if (m_scene.PhysicsScene != null) { lock (m_lastReportedExtraSimStats) { m_lastReportedExtraSimStats["LastReportedObjectUpdates"] = m_objectUpdates * updateTimeFactor; m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value; Dictionary <string, float> physicsStats = m_scene.PhysicsScene.GetStats(); if (physicsStats != null) { foreach (KeyValuePair <string, float> tuple in physicsStats) { // FIXME: An extremely dirty hack to divide MS stats per frame rather than per second // Need to change things so that stats source can indicate whether they are per second or // per frame. if (tuple.Key.EndsWith("MS")) { m_lastReportedExtraSimStats[tuple.Key] = tuple.Value * perframefactor; } else { m_lastReportedExtraSimStats[tuple.Key] = tuple.Value * updateTimeFactor; } } } } } // LastReportedObjectUpdates = m_objectUpdates / m_statsUpdateFactor; ResetValues(); Monitor.Exit(m_statsLock); } }