From fdb2a75ad357668b860fc5055e0630ef75a3ad20 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 17 Oct 2009 18:01:22 -0700 Subject: [PATCH] Committing the second part of Jim Greensky @ Intel Lab's patch, re-prioritizing updates --- .../Client/MXP/ClientStack/MXPClientView.cs | 4 + .../VWoHTTP/ClientStack/VWHClientView.cs | 5 + OpenSim/Framework/IClientAPI.cs | 27 +++++ .../ClientStack/LindenUDP/LLClientView.cs | 107 ++++++++++++++--- .../Examples/SimpleModule/MyNpcCharacter.cs | 4 + OpenSim/Region/Framework/Scenes/Scene.cs | 13 ++ OpenSim/Region/Framework/Scenes/SceneGraph.cs | 2 +- .../Region/Framework/Scenes/ScenePresence.cs | 112 +++++++++++++++++- .../Server/IRCClientView.cs | 5 + .../OptionalModules/World/NPC/NPCAvatar.cs | 4 + OpenSim/Tests/Common/Mock/TestClient.cs | 4 + 11 files changed, 267 insertions(+), 20 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index ea29c41b8f..52110d6ef8 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1042,6 +1042,10 @@ namespace OpenSim.Client.MXP.ClientStack Session.Send(me); } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index d6990de17a..4a54c679fd 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -596,6 +596,11 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + throw new System.NotImplementedException(); + } + public void FlushPrimUpdates() { throw new System.NotImplementedException(); diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 9aa3af1496..03e7a25786 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -448,6 +448,8 @@ namespace OpenSim.Framework public delegate void AvatarInterestUpdate(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); + public delegate double UpdatePriorityHandler(UpdatePriorityData data); + #endregion public struct DirPlacesReplyData @@ -744,6 +746,29 @@ namespace OpenSim.Framework public double priority { get { return this.m_priority; } } } + public struct UpdatePriorityData { + private double m_priority; + private uint m_localID; + + public UpdatePriorityData(double priority, uint localID) { + this.m_priority = priority; + this.m_localID = localID; + } + + public double priority { get { return this.m_priority; } } + public uint localID { get { return this.m_localID; } } + } + + [Flags] + public enum StateUpdateTypes + { + None = 0, + AvatarTerse = 1, + PrimitiveTerse = AvatarTerse << 1, + PrimitiveFull = PrimitiveTerse << 1, + All = AvatarTerse | PrimitiveTerse | PrimitiveFull, + } + public interface IClientAPI { Vector3 StartPos { get; set; } @@ -1118,6 +1143,8 @@ namespace OpenSim.Framework void SendPrimTerseUpdate(SendPrimitiveTerseData data); + void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler); + void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, bool fetchFolders, bool fetchItems); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 43c3c7c75b..3b2a6042f4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3582,37 +3582,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP void HandleQueueEmpty(ThrottleOutPacketType queue) { - int count = 0; - switch (queue) { case ThrottleOutPacketType.Texture: ProcessTextureRequests(); break; case ThrottleOutPacketType.Task: - lock (m_avatarTerseUpdates.SyncRoot) - count = m_avatarTerseUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_avatarTerseUpdates.SyncRoot, 1)) { - ProcessAvatarTerseUpdates(); - return; + try + { + if (m_avatarTerseUpdates.Count > 0) + { + + ProcessAvatarTerseUpdates(); + return; + } + } + finally { Monitor.Exit(m_avatarTerseUpdates.SyncRoot); } } break; case ThrottleOutPacketType.State: - lock (m_primFullUpdates.SyncRoot) - count = m_primFullUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_primFullUpdates.SyncRoot, 1)) { - ProcessPrimFullUpdates(); - return; + try + { + if (m_primFullUpdates.Count > 0) + { + ProcessPrimFullUpdates(); + return; + } + } + finally { Monitor.Exit(m_primFullUpdates.SyncRoot); } } - lock (m_primTerseUpdates.SyncRoot) - count = m_primTerseUpdates.Count; - if (count > 0) + if (Monitor.TryEnter(m_primTerseUpdates.SyncRoot, 1)) { - ProcessPrimTerseUpdates(); - return; + try + { + if (m_primTerseUpdates.Count > 0) + { + ProcessPrimTerseUpdates(); + return; + } + } + finally { Monitor.Exit(m_primTerseUpdates.SyncRoot); } } break; } @@ -3703,6 +3717,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + PriorityQueue.UpdatePriorityHandler terse_update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + PriorityQueue.UpdatePriorityHandler update_priority_handler = + delegate(ref double priority, uint local_id) + { + priority = handler(new UpdatePriorityData(priority, local_id)); + return priority != double.NaN; + }; + + if ((type & StateUpdateTypes.AvatarTerse) != 0) { + lock (m_avatarTerseUpdates.SyncRoot) + m_avatarTerseUpdates.Reprioritize(terse_update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveFull) != 0) { + lock (m_primFullUpdates.SyncRoot) + m_primFullUpdates.Reprioritize(update_priority_handler); + } + + if ((type & StateUpdateTypes.PrimitiveTerse) != 0) { + lock (m_primTerseUpdates.SyncRoot) + m_primTerseUpdates.Reprioritize(terse_update_priority_handler); + } + } + public void FlushPrimUpdates() { while (m_primFullUpdates.Count > 0) @@ -10465,6 +10510,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region PriorityQueue private class PriorityQueue { + internal delegate bool UpdatePriorityHandler(ref TPriority priority, uint local_id); + private MinHeap[] heaps = new MinHeap[1]; private Dictionary lookup_table = new Dictionary(); private Comparison comparison; @@ -10539,6 +10586,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP throw new InvalidOperationException(string.Format("The {0} is empty", this.GetType().ToString())); } + internal void Reprioritize(UpdatePriorityHandler handler) + { + MinHeapItem item; + TPriority priority; + + foreach (LookupItem lookup in new List(this.lookup_table.Values)) + { + if (lookup.Heap.TryGetValue(lookup.Handle, out item)) + { + priority = item.Priority; + if (handler(ref priority, item.LocalID)) + { + if (lookup.Heap.ContainsHandle(lookup.Handle)) + lookup.Heap[lookup.Handle] = + new MinHeapItem(priority, item.Value, item.LocalID); + } + else + { + m_log.Warn("[LLClientView] UpdatePriorityHandle returned false, dropping update"); + lookup.Heap.Remove(lookup.Handle); + this.lookup_table.Remove(item.LocalID); + } + } + } + } + #region MinHeapItem private struct MinHeapItem : IComparable { diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 3799a02f84..5a5fcfe8c5 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -527,6 +527,10 @@ namespace OpenSim.Region.Examples.SimpleModule { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 49c1ebf929..30fe976490 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -278,6 +278,10 @@ namespace OpenSim.Region.Framework.Scenes private bool m_firstHeartbeat = true; private UpdatePrioritizationSchemes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; + private bool m_reprioritization_enabled = true; + private double m_reprioritization_interval = 2000.0; + private double m_root_reprioritization_distance = 5.0; + private double m_child_reprioritization_distance = 10.0; private object m_deleting_scene_object = new object(); @@ -291,6 +295,10 @@ namespace OpenSim.Region.Framework.Scenes #region Properties public UpdatePrioritizationSchemes UpdatePrioritizationScheme { get { return this.m_update_prioritization_scheme; } } + public bool IsReprioritizationEnabled { get { return m_reprioritization_enabled; } } + public double ReprioritizationInterval { get { return m_reprioritization_interval; } } + public double RootReprioritizationDistance { get { return m_root_reprioritization_distance; } } + public double ChildReprioritizationDistance { get { return m_child_reprioritization_distance; } } public AgentCircuitManager AuthenticateHandler { @@ -542,6 +550,11 @@ namespace OpenSim.Region.Framework.Scenes m_update_prioritization_scheme = UpdatePrioritizationSchemes.Time; break; } + + m_reprioritization_enabled = interest_management_config.GetBoolean("ReprioritizationEnabled", true); + m_reprioritization_interval = interest_management_config.GetDouble("ReprioritizationInterval", 5000.0); + m_root_reprioritization_distance = interest_management_config.GetDouble("RootReprioritizationDistance", 10.0); + m_child_reprioritization_distance = interest_management_config.GetDouble("ChildReprioritizationDistance", 20.0); } m_log.Info("[SCENE]: Using the " + m_update_prioritization_scheme + " prioritization scheme"); diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index b9872ca0cf..8ee26c3cd7 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -846,7 +846,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// null if no scene object group containing that prim is found - private SceneObjectGroup GetGroupByPrim(uint localID) + public SceneObjectGroup GetGroupByPrim(uint localID) { if (Entities.ContainsKey(localID)) return Entities[localID] as SceneObjectGroup; diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 9d13ad4649..f05c3d8183 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Timers; using OpenMetaverse; using log4net; using OpenSim.Framework; @@ -172,6 +173,11 @@ namespace OpenSim.Region.Framework.Scenes // Position of agent's camera in world (region cordinates) protected Vector3 m_CameraCenter = Vector3.Zero; + protected Vector3 m_lastCameraCenter = Vector3.Zero; + + protected Timer m_reprioritization_timer; + protected bool m_reprioritizing = false; + protected bool m_reprioritization_called = false; // Use these three vectors to figure out what the agent is looking at // Convert it to a Matrix and/or Quaternion @@ -639,7 +645,14 @@ namespace OpenSim.Region.Framework.Scenes m_scriptEngines = m_scene.RequestModuleInterfaces(); - AbsolutePosition = m_controllingClient.StartPos; + AbsolutePosition = posLastSignificantMove = m_CameraCenter = + m_lastCameraCenter = m_controllingClient.StartPos; + + m_reprioritization_timer = new Timer(world.ReprioritizationInterval); + m_reprioritization_timer.Elapsed += new ElapsedEventHandler(Reprioritize); + m_reprioritization_timer.AutoReset = false; + + AdjustKnownSeeds(); TrySetMovementAnimation("STAND"); // TODO: I think, this won't send anything, as we are still a child here... @@ -1219,6 +1232,11 @@ namespace OpenSim.Region.Framework.Scenes // Camera location in world. We'll need to raytrace // from this location from time to time. m_CameraCenter = agentData.CameraCenter; + if (Vector3.Distance(m_lastCameraCenter, m_CameraCenter) >= Scene.RootReprioritizationDistance) + { + ReprioritizeUpdates(); + m_lastCameraCenter = m_CameraCenter; + } // Use these three vectors to figure out what the agent is looking at // Convert it to a Matrix and/or Quaternion @@ -2823,7 +2841,7 @@ namespace OpenSim.Region.Framework.Scenes } // Minimum Draw distance is 64 meters, the Radius of the draw distance sphere is 32m - if (Util.GetDistanceTo(AbsolutePosition,m_LastChildAgentUpdatePosition) > 32) + if (Util.GetDistanceTo(AbsolutePosition, m_LastChildAgentUpdatePosition) >= Scene.ChildReprioritizationDistance) { ChildAgentDataUpdate cadu = new ChildAgentDataUpdate(); cadu.ActiveGroupID = UUID.Zero.Guid; @@ -3118,6 +3136,12 @@ namespace OpenSim.Region.Framework.Scenes if (cAgentData.Position != new Vector3(-1, -1, -1)) // UGH!! m_pos = new Vector3(cAgentData.Position.X + shiftx, cAgentData.Position.Y + shifty, cAgentData.Position.Z); + if (Vector3.Distance(AbsolutePosition, posLastSignificantMove) >= Scene.ChildReprioritizationDistance) + { + posLastSignificantMove = AbsolutePosition; + ReprioritizeUpdates(); + } + // It's hard to say here.. We can't really tell where the camera position is unless it's in world cordinates from the sending region m_CameraCenter = cAgentData.Center; @@ -3498,6 +3522,16 @@ namespace OpenSim.Region.Framework.Scenes { m_knownChildRegions.Clear(); } + + lock (m_reprioritization_timer) + { + m_reprioritization_timer.Enabled = false; + m_reprioritization_timer.Elapsed -= new ElapsedEventHandler(Reprioritize); + } + // I don't get it but mono crashes when you try to dispose of this timer, + // unsetting the elapsed callback should be enough to allow for cleanup however. + //m_reprioritizationTimer.Dispose(); + m_sceneViewer.Close(); RemoveFromPhysicalScene(); @@ -3913,5 +3947,79 @@ namespace OpenSim.Region.Framework.Scenes { return Vector3.Distance(AbsolutePosition, position); } + + private double GetSOGUpdatePriority(SceneObjectGroup sog) + { + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + throw new InvalidOperationException("UpdatePrioritizationScheme for time not supported for reprioritization"); + case Scene.UpdatePrioritizationSchemes.Distance: + return sog.GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return sog.GetPriorityBySimpleAngularDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); + } + } + + private double UpdatePriority(UpdatePriorityData data) + { + EntityBase entity; + SceneObjectGroup group; + + if (Scene.Entities.TryGetValue(data.localID, out entity)) + { + group = entity as SceneObjectGroup; + if (group != null) + return GetSOGUpdatePriority(group); + + ScenePresence presence = entity as ScenePresence; + if (presence == null) + throw new InvalidOperationException("entity found is neither SceneObjectGroup nor ScenePresence"); + switch (Scene.UpdatePrioritizationScheme) + { + case Scene.UpdatePrioritizationSchemes.Time: + throw new InvalidOperationException("UpdatePrioritization for time not supported for reprioritization"); + case Scene.UpdatePrioritizationSchemes.Distance: + case Scene.UpdatePrioritizationSchemes.SimpleAngularDistance: + return GetPriorityByDistance((IsChildAgent) ? AbsolutePosition : CameraPosition); + default: + throw new InvalidOperationException("UpdatePrioritizationScheme not defined"); + } + } + else + { + group = Scene.SceneGraph.GetGroupByPrim(data.localID); + if (group != null) + return GetSOGUpdatePriority(group); + } + return double.NaN; + } + + private void ReprioritizeUpdates() + { + if (Scene.IsReprioritizationEnabled && Scene.UpdatePrioritizationScheme != Scene.UpdatePrioritizationSchemes.Time) + { + lock (m_reprioritization_timer) + { + if (!m_reprioritizing) + m_reprioritization_timer.Enabled = m_reprioritizing = true; + else + m_reprioritization_called = true; + } + } + } + + private void Reprioritize(object sender, ElapsedEventArgs e) + { + m_controllingClient.ReprioritizeUpdates(StateUpdateTypes.All, UpdatePriority); + + lock (m_reprioritization_timer) + { + m_reprioritization_timer.Enabled = m_reprioritizing = m_reprioritization_called; + m_reprioritization_called = false; + } + } } } diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 1a24decb21..df03b8d934 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1048,6 +1048,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } + public void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + + } + public void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, List folders, bool fetchFolders, bool fetchItems) { diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 6c58f2dcb4..f7cadaabd2 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -616,6 +616,10 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 21606e24d0..0f642b945f 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -620,6 +620,10 @@ namespace OpenSim.Tests.Common.Mock { } + public virtual void ReprioritizeUpdates(StateUpdateTypes type, UpdatePriorityHandler handler) + { + } + public void FlushPrimUpdates() { }