OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
Prioritizer.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using log4net;
31 using Nini.Config;
32 using OpenSim.Framework;
33 using OpenMetaverse;
34 using OpenSim.Region.PhysicsModules.SharedBase;
35 
36 /*
37  * Steps to add a new prioritization policy:
38  *
39  * - Add a new value to the UpdatePrioritizationSchemes enum.
40  * - Specify this new value in the [InterestManagement] section of your
41  * OpenSim.ini. The name in the config file must match the enum value name
42  * (although it is not case sensitive).
43  * - Write a new GetPriorityBy*() method in this class.
44  * - Add a new entry to the switch statement in GetUpdatePriority() that calls
45  * your method.
46  */
47 
48 namespace OpenSim.Region.Framework.Scenes
49 {
51  {
52  Time = 0,
53  Distance = 1,
55  FrontBack = 3,
57  }
58 
59  public class Prioritizer
60  {
61  private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
62 
63  private Scene m_scene;
64 
65  public Prioritizer(Scene scene)
66  {
67  m_scene = scene;
68  }
69 
80  public uint GetUpdatePriority(IClientAPI client, ISceneEntity entity)
81  {
82  // If entity is null we have a serious problem
83  if (entity == null)
84  {
85  m_log.WarnFormat("[PRIORITIZER] attempt to prioritize null entity");
86  throw new InvalidOperationException("Prioritization entity not defined");
87  }
88 
89  // If this is an update for our own avatar give it the highest priority
90  if (client.AgentId == entity.UUID)
91  return 0;
92 
93  uint priority;
94 
95  switch (m_scene.UpdatePrioritizationScheme)
96  {
97 /*
98  case UpdatePrioritizationSchemes.Time:
99  priority = GetPriorityByTime(client, entity);
100  break;
101  case UpdatePrioritizationSchemes.Distance:
102  priority = GetPriorityByDistance(client, entity);
103  break;
104  case UpdatePrioritizationSchemes.SimpleAngularDistance:
105  priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance
106  break;
107  case UpdatePrioritizationSchemes.FrontBack:
108  priority = GetPriorityByFrontBack(client, entity);
109  break;
110 */
111  case UpdatePrioritizationSchemes.BestAvatarResponsiveness:
112  default:
113  priority = GetPriorityByBestAvatarResponsiveness(client, entity);
114  break;
115  }
116 
117  return priority;
118  }
119 
120  private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity)
121  {
122  // And anything attached to this avatar gets top priority as well
123  if (entity is SceneObjectPart)
124  {
125  SceneObjectPart sop = (SceneObjectPart)entity;
126  if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar)
127  return 1;
128  }
129 
130  return PriorityQueue.NumberOfImmediateQueues; // first queue past the immediate queues
131  }
132 
133  private uint GetPriorityByDistance(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 
143  return ComputeDistancePriority(client,entity,false);
144  }
145 
146  private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity)
147  {
148  // And anything attached to this avatar gets top priority as well
149  if (entity is SceneObjectPart)
150  {
151  SceneObjectPart sop = (SceneObjectPart)entity;
152  if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar)
153  return 1;
154  }
155 
156  return ComputeDistancePriority(client,entity,true);
157  }
158 
159  private uint GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity)
160  {
161  uint pqueue = 2; // keep compiler happy
162 
163  ScenePresence presence = m_scene.GetScenePresence(client.AgentId);
164  if (presence != null)
165  {
166  // All avatars other than our own go into pqueue 1
167  if (entity is ScenePresence)
168  return 1;
169 
170  if (entity is SceneObjectPart)
171  {
172  // Attachments are high priority,
173  if (((SceneObjectPart)entity).ParentGroup.IsAttachment)
174  return 2;
175 
176  pqueue = ComputeDistancePriority(client, entity, false);
177 
178  // Non physical prims are lower priority than physical prims
179  PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor;
180  if (physActor == null || !physActor.IsPhysical)
181  pqueue++;
182  }
183  }
184  else
185  pqueue = ComputeDistancePriority(client, entity, false);
186 
187  return pqueue;
188  }
189 
190  private uint ComputeDistancePriority(IClientAPI client, ISceneEntity entity, bool useFrontBack)
191  {
192  // Get this agent's position
193  ScenePresence presence = m_scene.GetScenePresence(client.AgentId);
194  if (presence == null)
195  {
196  // this shouldn't happen, it basically means that we are prioritizing
197  // updates to send to a client that doesn't have a presence in the scene
198  // seems like there's race condition here...
199 
200  // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId);
201  // throw new InvalidOperationException("Prioritization agent not defined");
202  return PriorityQueue.NumberOfQueues - 1;
203  }
204 
205  // Use group position for child prims, since we are putting child prims in
206  // the same queue with the root of the group, the root prim (which goes into
207  // the queue first) should always be sent first, no need to adjust child prim
208  // priorities
209  Vector3 entityPos = entity.AbsolutePosition;
210  if (entity is SceneObjectPart)
211  {
212  SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup;
213  entityPos = group.AbsolutePosition;
214  }
215 
216  // Use the camera position for local agents and avatar position for remote agents
217  // Why would I want that? They could be camming but I still see them at the
218  // avatar position, so why should I update them as if they were at their
219  // camera positions? Makes no sense!
220  // TODO: Fix this mess
221  //Vector3 presencePos = (presence.IsChildAgent) ?
222  // presence.AbsolutePosition :
223  // presence.CameraPosition;
224 
225  Vector3 presencePos = presence.AbsolutePosition;
226 
227  // Compute the distance...
228  double distance = Vector3.Distance(presencePos, entityPos);
229 
230  // And convert the distance to a priority queue, this computation gives queues
231  // at 10, 20, 40, 80, 160, 320, 640, and 1280m
232  uint pqueue = PriorityQueue.NumberOfImmediateQueues + 1; // reserve attachments queue
233  uint queues = PriorityQueue.NumberOfQueues - PriorityQueue.NumberOfImmediateQueues;
234 /*
235  for (int i = 0; i < queues - 1; i++)
236  {
237  if (distance < 30 * Math.Pow(2.0,i))
238  break;
239  pqueue++;
240  }
241 */
242  if (distance > 10f)
243  {
244  float tmp = (float)Math.Log((double)distance) * 1.4426950408889634073599246810019f - 3.3219280948873623478703194294894f;
245  // for a map identical to original:
246  // now
247  // 1st constant is 1/(log(2)) (natural log) so we get log2(distance)
248  // 2st constant makes it be log2(distance/10)
249  pqueue += (uint)tmp;
250  if (pqueue > queues - 1)
251  pqueue = queues - 1;
252  }
253 
254  // If this is a root agent, then determine front & back
255  // Bump up the priority queue (drop the priority) for any objects behind the avatar
256  if (useFrontBack && ! presence.IsChildAgent)
257  {
258  // Root agent, decrease priority for objects behind us
259  Vector3 camPosition = presence.CameraPosition;
260  Vector3 camAtAxis = presence.CameraAtAxis;
261 
262  // Plane equation
263  float d = -Vector3.Dot(camPosition, camAtAxis);
264  float p = Vector3.Dot(camAtAxis, entityPos) + d;
265  if (p < 0.0f)
266  pqueue++;
267  }
268 
269  return pqueue;
270  }
271 
272  }
273 }
uint GetUpdatePriority(IClientAPI client, ISceneEntity entity)
Returns the priority queue into which the update should be placed. Updates within a queue will be pro...
Definition: Prioritizer.cs:80
UUID AttachedAvatar
The avatar to which this scene object is attached.
bool IsAttachment
Is this scene object acting as an attachment?