Prioritizer.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using log4net;
  30. using Nini.Config;
  31. using OpenSim.Framework;
  32. using OpenMetaverse;
  33. using OpenSim.Region.PhysicsModules.SharedBase;
  34. /*
  35. * Steps to add a new prioritization policy:
  36. *
  37. * - Add a new value to the UpdatePrioritizationSchemes enum.
  38. * - Specify this new value in the [InterestManagement] section of your
  39. * OpenSim.ini. The name in the config file must match the enum value name
  40. * (although it is not case sensitive).
  41. * - Write a new GetPriorityBy*() method in this class.
  42. * - Add a new entry to the switch statement in GetUpdatePriority() that calls
  43. * your method.
  44. */
  45. namespace OpenSim.Region.Framework.Scenes
  46. {
  47. public enum UpdatePrioritizationSchemes
  48. {
  49. Time = 0,
  50. Distance = 1,
  51. SimpleAngularDistance = 2,
  52. FrontBack = 3,
  53. BestAvatarResponsiveness = 4,
  54. }
  55. public class Prioritizer
  56. {
  57. private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  58. private Scene m_scene;
  59. public Prioritizer(Scene scene)
  60. {
  61. m_scene = scene;
  62. }
  63. /// <summary>
  64. /// Returns the priority queue into which the update should be placed. Updates within a
  65. /// queue will be processed in arrival order. There are currently 12 priority queues
  66. /// implemented in PriorityQueue class in LLClientView. Queue 0 is generally retained
  67. /// for avatar updates. The fair queuing discipline for processing the priority queues
  68. /// assumes that the number of entities in each priority queues increases exponentially.
  69. /// So for example... if queue 1 contains all updates within 10m of the avatar or camera
  70. /// then queue 2 at 20m is about 3X bigger in space & about 3X bigger in total number
  71. /// of updates.
  72. /// </summary>
  73. public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity)
  74. {
  75. // If entity is null we have a serious problem
  76. if (entity == null)
  77. {
  78. m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity");
  79. throw new InvalidOperationException("Prioritization entity not defined");
  80. }
  81. // If this is an update for our own avatar give it the highest priority
  82. if (client.AgentId == entity.UUID)
  83. return 0;
  84. uint priority;
  85. switch (m_scene.UpdatePrioritizationScheme)
  86. {
  87. /*
  88. case UpdatePrioritizationSchemes.Time:
  89. priority = GetPriorityByTime(client, entity);
  90. break;
  91. case UpdatePrioritizationSchemes.Distance:
  92. priority = GetPriorityByDistance(client, entity);
  93. break;
  94. case UpdatePrioritizationSchemes.SimpleAngularDistance:
  95. priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance
  96. break;
  97. case UpdatePrioritizationSchemes.FrontBack:
  98. priority = GetPriorityByFrontBack(client, entity);
  99. break;
  100. */
  101. case UpdatePrioritizationSchemes.SimpleAngularDistance:
  102. priority = GetPriorityByAngularDistance(client, entity); // TODO: Reimplement SimpleAngularDistance
  103. break;
  104. case UpdatePrioritizationSchemes.BestAvatarResponsiveness:
  105. default:
  106. priority = GetPriorityByBestAvatarResponsiveness(client, entity);
  107. break;
  108. }
  109. return priority;
  110. }
  111. private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity)
  112. {
  113. // And anything attached to this avatar gets top priority as well
  114. if (entity is SceneObjectPart)
  115. {
  116. SceneObjectPart sop = (SceneObjectPart)entity;
  117. if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar)
  118. return 1;
  119. }
  120. return PriorityQueue.NumberOfImmediateQueues; // first queue past the immediate queues
  121. }
  122. private uint GetPriorityByDistance(IClientAPI client, ISceneEntity entity)
  123. {
  124. // And anything attached to this avatar gets top priority as well
  125. if (entity is SceneObjectPart)
  126. {
  127. SceneObjectPart sop = (SceneObjectPart)entity;
  128. if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar)
  129. return 1;
  130. }
  131. return ComputeDistancePriority(client,entity,false);
  132. }
  133. private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity)
  134. {
  135. // And anything attached to this avatar gets top priority as well
  136. if (entity is SceneObjectPart)
  137. {
  138. SceneObjectPart sop = (SceneObjectPart)entity;
  139. if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar)
  140. return 1;
  141. }
  142. return ComputeDistancePriority(client,entity,true);
  143. }
  144. private uint GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity)
  145. {
  146. uint pqueue = 2; // keep compiler happy
  147. ScenePresence presence = m_scene.GetScenePresence(client.AgentId);
  148. if (presence != null)
  149. {
  150. // All avatars other than our own go into pqueue 1
  151. if (entity is ScenePresence)
  152. return 1;
  153. if (entity is SceneObjectPart)
  154. {
  155. SceneObjectGroup sog = ((SceneObjectPart)entity).ParentGroup;
  156. // Attachments are high priority,
  157. if (sog.IsAttachment)
  158. return 2;
  159. if(presence.ParentPart != null)
  160. {
  161. if(presence.ParentPart.ParentGroup == sog)
  162. return 2;
  163. }
  164. pqueue = ComputeDistancePriority(client, entity, false);
  165. // Non physical prims are lower priority than physical prims
  166. PhysicsActor physActor = sog.RootPart.PhysActor;
  167. if (physActor == null || !physActor.IsPhysical)
  168. pqueue++;
  169. }
  170. }
  171. else
  172. pqueue = ComputeDistancePriority(client, entity, false);
  173. return pqueue;
  174. }
  175. private uint ComputeDistancePriority(IClientAPI client, ISceneEntity entity, bool useFrontBack)
  176. {
  177. // Get this agent's position
  178. ScenePresence presence = m_scene.GetScenePresence(client.AgentId);
  179. if (presence == null)
  180. {
  181. // this shouldn't happen, it basically means that we are prioritizing
  182. // updates to send to a client that doesn't have a presence in the scene
  183. // seems like there's race condition here...
  184. // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId);
  185. // throw new InvalidOperationException("Prioritization agent not defined");
  186. return PriorityQueue.NumberOfQueues - 1;
  187. }
  188. // Use group position for child prims, since we are putting child prims in
  189. // the same queue with the root of the group, the root prim (which goes into
  190. // the queue first) should always be sent first, no need to adjust child prim
  191. // priorities
  192. Vector3 entityPos = entity.AbsolutePosition;
  193. if (entity is SceneObjectPart)
  194. {
  195. SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup;
  196. entityPos = group.AbsolutePosition;
  197. }
  198. // Use the camera position for local agents and avatar position for remote agents
  199. // Why would I want that? They could be camming but I still see them at the
  200. // avatar position, so why should I update them as if they were at their
  201. // camera positions? Makes no sense!
  202. // TODO: Fix this mess
  203. //Vector3 presencePos = (presence.IsChildAgent) ?
  204. // presence.AbsolutePosition :
  205. // presence.CameraPosition;
  206. Vector3 presencePos = presence.AbsolutePosition;
  207. // Compute the distance...
  208. double distance = Vector3.Distance(presencePos, entityPos);
  209. // And convert the distance to a priority queue, this computation gives queues
  210. // at 10, 20, 40, 80, 160, 320, 640, and 1280m
  211. uint pqueue = PriorityQueue.NumberOfImmediateQueues + 1; // reserve attachments queue
  212. uint queues = PriorityQueue.NumberOfQueues - PriorityQueue.NumberOfImmediateQueues;
  213. /*
  214. for (int i = 0; i < queues - 1; i++)
  215. {
  216. if (distance < 30 * Math.Pow(2.0,i))
  217. break;
  218. pqueue++;
  219. }
  220. */
  221. if (distance > 10f)
  222. {
  223. float tmp = (float)Math.Log((double)distance) * 1.442695f - 3.321928f;
  224. // for a map identical to original:
  225. // now
  226. // 1st constant is 1/(log(2)) (natural log) so we get log2(distance)
  227. // 2st constant makes it be log2(distance/10)
  228. pqueue += (uint)tmp;
  229. if (pqueue > queues - 1)
  230. pqueue = queues - 1;
  231. }
  232. // If this is a root agent, then determine front & back
  233. // Bump up the priority queue (drop the priority) for any objects behind the avatar
  234. if (useFrontBack && ! presence.IsChildAgent)
  235. {
  236. // Root agent, decrease priority for objects behind us
  237. Vector3 camPosition = presence.CameraPosition;
  238. Vector3 camAtAxis = presence.CameraAtAxis;
  239. // Plane equation
  240. float d = -Vector3.Dot(camPosition, camAtAxis);
  241. float p = Vector3.Dot(camAtAxis, entityPos) + d;
  242. if (p < 0.0f)
  243. pqueue++;
  244. }
  245. return pqueue;
  246. }
  247. private uint GetPriorityByAngularDistance(IClientAPI client, ISceneEntity entity)
  248. {
  249. ScenePresence presence = m_scene.GetScenePresence(client.AgentId);
  250. if (presence == null)
  251. return PriorityQueue.NumberOfQueues - 1;
  252. uint pqueue = ComputeAngleDistancePriority(presence, entity);
  253. return pqueue;
  254. }
  255. private uint ComputeAngleDistancePriority(ScenePresence presence, ISceneEntity entity)
  256. {
  257. // And convert the distance to a priority queue, this computation gives queues
  258. // at 10, 20, 40, 80, 160, 320, 640, and 1280m
  259. // uint minpqueue = PriorityQueue.NumberOfImmediateQueues;
  260. uint maxqueue = PriorityQueue.NumberOfQueues - PriorityQueue.NumberOfImmediateQueues -1;
  261. // uint pqueue = minpqueue;
  262. uint pqueue = PriorityQueue.NumberOfImmediateQueues;
  263. float distance;
  264. Vector3 presencePos = presence.AbsolutePosition;
  265. if(entity is ScenePresence)
  266. {
  267. ScenePresence sp = entity as ScenePresence;
  268. distance = Vector3.Distance(presencePos, sp.AbsolutePosition);
  269. distance *= 0.5f;
  270. }
  271. else
  272. {
  273. SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup;
  274. if(presence.ParentPart != null)
  275. {
  276. if(presence.ParentPart.ParentGroup == group)
  277. return pqueue;
  278. }
  279. if(group.IsAttachment)
  280. {
  281. if(group.RootPart.LocalId == presence.LocalId)
  282. return pqueue;
  283. }
  284. float bradius = group.GetBoundsRadius();
  285. Vector3 grppos = group.AbsolutePosition + group.getBoundsCenter();
  286. distance = Vector3.Distance(presencePos, grppos);
  287. distance -= bradius;
  288. distance *= group.getAreaFactor();
  289. if(group.IsAttachment)
  290. distance *= 0.5f;
  291. else if(group.UsesPhysics)
  292. distance *= 0.6f;
  293. else if(group.GetSittingAvatarsCount() > 0)
  294. distance *= 0.5f;
  295. }
  296. if (distance > 10f)
  297. {
  298. float tmp = (float)Math.Log(distance) * 1.442695f - 3.321928f;
  299. // for a map identical to original:
  300. // now
  301. // 1st constant is 1/(log(2)) (natural log) so we get log2(distance)
  302. // 2st constant makes it be log2(distance/10)
  303. pqueue += (uint)tmp;
  304. if (pqueue > maxqueue)
  305. pqueue = maxqueue;
  306. }
  307. return pqueue;
  308. }
  309. }
  310. }