diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/Prioritizer.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Prioritizer.cs | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs new file mode 100644 index 0000000..1b10e3c --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs | |||
@@ -0,0 +1,253 @@ | |||
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.Physics.Manager; | ||
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 | { | ||
50 | public enum UpdatePrioritizationSchemes | ||
51 | { | ||
52 | Time = 0, | ||
53 | Distance = 1, | ||
54 | SimpleAngularDistance = 2, | ||
55 | FrontBack = 3, | ||
56 | BestAvatarResponsiveness = 4, | ||
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 | |||
70 | /// <summary> | ||
71 | /// Returns the priority queue into which the update should be placed. Updates within a | ||
72 | /// queue will be processed in arrival order. There are currently 12 priority queues | ||
73 | /// implemented in PriorityQueue class in LLClientView. Queue 0 is generally retained | ||
74 | /// for avatar updates. The fair queuing discipline for processing the priority queues | ||
75 | /// assumes that the number of entities in each priority queues increases exponentially. | ||
76 | /// So for example... if queue 1 contains all updates within 10m of the avatar or camera | ||
77 | /// then queue 2 at 20m is about 3X bigger in space & about 3X bigger in total number | ||
78 | /// of updates. | ||
79 | /// </summary> | ||
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 | case UpdatePrioritizationSchemes.Time: | ||
98 | priority = GetPriorityByTime(client, entity); | ||
99 | break; | ||
100 | case UpdatePrioritizationSchemes.Distance: | ||
101 | priority = GetPriorityByDistance(client, entity); | ||
102 | break; | ||
103 | case UpdatePrioritizationSchemes.SimpleAngularDistance: | ||
104 | priority = GetPriorityByDistance(client, entity); // TODO: Reimplement SimpleAngularDistance | ||
105 | break; | ||
106 | case UpdatePrioritizationSchemes.FrontBack: | ||
107 | priority = GetPriorityByFrontBack(client, entity); | ||
108 | break; | ||
109 | case UpdatePrioritizationSchemes.BestAvatarResponsiveness: | ||
110 | priority = GetPriorityByBestAvatarResponsiveness(client, entity); | ||
111 | break; | ||
112 | default: | ||
113 | throw new InvalidOperationException("UpdatePrioritizationScheme not defined."); | ||
114 | } | ||
115 | |||
116 | return priority; | ||
117 | } | ||
118 | |||
119 | private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity) | ||
120 | { | ||
121 | // And anything attached to this avatar gets top priority as well | ||
122 | if (entity is SceneObjectPart) | ||
123 | { | ||
124 | SceneObjectPart sop = (SceneObjectPart)entity; | ||
125 | if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar) | ||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | return PriorityQueue.NumberOfImmediateQueues; // first queue past the immediate queues | ||
130 | } | ||
131 | |||
132 | private uint GetPriorityByDistance(IClientAPI client, ISceneEntity entity) | ||
133 | { | ||
134 | // And anything attached to this avatar gets top priority as well | ||
135 | if (entity is SceneObjectPart) | ||
136 | { | ||
137 | SceneObjectPart sop = (SceneObjectPart)entity; | ||
138 | if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar) | ||
139 | return 1; | ||
140 | } | ||
141 | |||
142 | return ComputeDistancePriority(client,entity,false); | ||
143 | } | ||
144 | |||
145 | private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity) | ||
146 | { | ||
147 | // And anything attached to this avatar gets top priority as well | ||
148 | if (entity is SceneObjectPart) | ||
149 | { | ||
150 | SceneObjectPart sop = (SceneObjectPart)entity; | ||
151 | if (sop.ParentGroup.IsAttachment && client.AgentId == sop.ParentGroup.AttachedAvatar) | ||
152 | return 1; | ||
153 | } | ||
154 | |||
155 | return ComputeDistancePriority(client,entity,true); | ||
156 | } | ||
157 | |||
158 | private uint GetPriorityByBestAvatarResponsiveness(IClientAPI client, ISceneEntity entity) | ||
159 | { | ||
160 | uint pqueue = ComputeDistancePriority(client,entity,true); | ||
161 | |||
162 | ScenePresence presence = m_scene.GetScenePresence(client.AgentId); | ||
163 | if (presence != null) | ||
164 | { | ||
165 | if (!presence.IsChildAgent) | ||
166 | { | ||
167 | // All avatars other than our own go into pqueue 1 | ||
168 | if (entity is ScenePresence) | ||
169 | return 1; | ||
170 | |||
171 | if (entity is SceneObjectPart) | ||
172 | { | ||
173 | // Attachments are high priority, | ||
174 | if (((SceneObjectPart)entity).ParentGroup.IsAttachment) | ||
175 | return 1; | ||
176 | |||
177 | // Non physical prims are lower priority than physical prims | ||
178 | PhysicsActor physActor = ((SceneObjectPart)entity).ParentGroup.RootPart.PhysActor; | ||
179 | if (physActor == null || !physActor.IsPhysical) | ||
180 | pqueue++; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | return pqueue; | ||
186 | } | ||
187 | |||
188 | private uint ComputeDistancePriority(IClientAPI client, ISceneEntity entity, bool useFrontBack) | ||
189 | { | ||
190 | // Get this agent's position | ||
191 | ScenePresence presence = m_scene.GetScenePresence(client.AgentId); | ||
192 | if (presence == null) | ||
193 | { | ||
194 | // this shouldn't happen, it basically means that we are prioritizing | ||
195 | // updates to send to a client that doesn't have a presence in the scene | ||
196 | // seems like there's race condition here... | ||
197 | |||
198 | // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); | ||
199 | // throw new InvalidOperationException("Prioritization agent not defined"); | ||
200 | return PriorityQueue.NumberOfQueues - 1; | ||
201 | } | ||
202 | |||
203 | // Use group position for child prims, since we are putting child prims in | ||
204 | // the same queue with the root of the group, the root prim (which goes into | ||
205 | // the queue first) should always be sent first, no need to adjust child prim | ||
206 | // priorities | ||
207 | Vector3 entityPos = entity.AbsolutePosition; | ||
208 | if (entity is SceneObjectPart) | ||
209 | { | ||
210 | SceneObjectGroup group = (entity as SceneObjectPart).ParentGroup; | ||
211 | entityPos = group.AbsolutePosition; | ||
212 | } | ||
213 | |||
214 | // Use the camera position for local agents and avatar position for remote agents | ||
215 | Vector3 presencePos = (presence.IsChildAgent) ? | ||
216 | presence.AbsolutePosition : | ||
217 | presence.CameraPosition; | ||
218 | |||
219 | // Compute the distance... | ||
220 | double distance = Vector3.Distance(presencePos, entityPos); | ||
221 | |||
222 | // And convert the distance to a priority queue, this computation gives queues | ||
223 | // at 10, 20, 40, 80, 160, 320, 640, and 1280m | ||
224 | uint pqueue = PriorityQueue.NumberOfImmediateQueues; | ||
225 | uint queues = PriorityQueue.NumberOfQueues - PriorityQueue.NumberOfImmediateQueues; | ||
226 | |||
227 | for (int i = 0; i < queues - 1; i++) | ||
228 | { | ||
229 | if (distance < 10 * Math.Pow(2.0,i)) | ||
230 | break; | ||
231 | pqueue++; | ||
232 | } | ||
233 | |||
234 | // If this is a root agent, then determine front & back | ||
235 | // Bump up the priority queue (drop the priority) for any objects behind the avatar | ||
236 | if (useFrontBack && ! presence.IsChildAgent) | ||
237 | { | ||
238 | // Root agent, decrease priority for objects behind us | ||
239 | Vector3 camPosition = presence.CameraPosition; | ||
240 | Vector3 camAtAxis = presence.CameraAtAxis; | ||
241 | |||
242 | // Plane equation | ||
243 | float d = -Vector3.Dot(camPosition, camAtAxis); | ||
244 | float p = Vector3.Dot(camAtAxis, entityPos) + d; | ||
245 | if (p < 0.0f) | ||
246 | pqueue++; | ||
247 | } | ||
248 | |||
249 | return pqueue; | ||
250 | } | ||
251 | |||
252 | } | ||
253 | } | ||