diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | 365 |
1 files changed, 168 insertions, 197 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 295a5e6..a484fdf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | |||
@@ -27,58 +27,60 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Threading; | 29 | using System.Threading; |
30 | using System.Collections; | ||
30 | using System.Collections.Generic; | 31 | using System.Collections.Generic; |
32 | using System.Reflection; | ||
31 | using OpenMetaverse; | 33 | using OpenMetaverse; |
32 | using OpenMetaverse.Imaging; | 34 | using OpenMetaverse.Imaging; |
33 | using OpenSim.Framework; | 35 | using OpenSim.Framework; |
34 | using OpenSim.Region.Framework.Interfaces; | 36 | using OpenSim.Region.Framework.Interfaces; |
35 | using OpenSim.Services.Interfaces; | 37 | using OpenSim.Services.Interfaces; |
36 | using log4net; | 38 | using log4net; |
37 | using System.Reflection; | ||
38 | 39 | ||
39 | namespace OpenSim.Region.ClientStack.LindenUDP | 40 | namespace OpenSim.Region.ClientStack.LindenUDP |
40 | { | 41 | { |
41 | |||
42 | public class LLImageManager | 42 | public class LLImageManager |
43 | { | 43 | { |
44 | 44 | private sealed class J2KImageComparer : IComparer<J2KImage> | |
45 | //Public interfaces: | 45 | { |
46 | //Constructor - (LLClientView, IAssetCache, IJ2KDecoder); | 46 | public int Compare(J2KImage x, J2KImage y) |
47 | //void EnqueueReq - (TextureRequestArgs) | 47 | { |
48 | //ProcessImageQueue | 48 | return x.m_requestedPriority.CompareTo(y.m_requestedPriority); |
49 | //Close | 49 | } |
50 | } | ||
51 | |||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
51 | private bool m_shuttingdown = false; | 53 | private bool m_shuttingdown = false; |
52 | private long m_lastloopprocessed = 0; | 54 | private long m_lastloopprocessed = 0; |
55 | private AssetBase m_missingImage = null; | ||
53 | 56 | ||
54 | private LLClientView m_client; //Client we're assigned to | 57 | private LLClientView m_client; //Client we're assigned to |
55 | private IAssetService m_assetCache; //Asset Cache | 58 | private IAssetService m_assetCache; //Asset Cache |
56 | private IJ2KDecoder m_j2kDecodeModule; //Our J2K module | 59 | private IJ2KDecoder m_j2kDecodeModule; //Our J2K module |
60 | private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer()); | ||
57 | 61 | ||
58 | private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes | ||
59 | private Dictionary<UUID,J2KImage> m_imagestore; // Our main image storage dictionary | ||
60 | private SortedList<double,UUID> m_priorities; // For fast image lookup based on priority | ||
61 | private Dictionary<int, int> m_priorityresolver; //Enabling super fast assignment of images with the same priorities | ||
62 | |||
63 | private const double doubleMinimum = .0000001; | ||
64 | |||
65 | public int m_outstandingtextures = 0; | ||
66 | //Constructor | ||
67 | public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule) | 62 | public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule) |
68 | { | 63 | { |
69 | |||
70 | m_imagestore = new Dictionary<UUID,J2KImage>(); | ||
71 | m_priorities = new SortedList<double,UUID>(); | ||
72 | m_priorityresolver = new Dictionary<int, int>(); | ||
73 | m_client = client; | 64 | m_client = client; |
74 | m_assetCache = pAssetCache; | 65 | m_assetCache = pAssetCache; |
75 | if (pAssetCache != null) | 66 | if (pAssetCache != null) |
76 | m_missingsubstitute = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); | 67 | m_missingImage = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); |
77 | else | 68 | else |
78 | m_log.Error("[ClientView] - couldn't set missing image, all manner of things will probably break"); | 69 | m_log.Error("[ClientView] - couldn't set missing image asset, falling back to missing image packet. This is known to crash the client"); |
70 | |||
79 | m_j2kDecodeModule = pJ2kDecodeModule; | 71 | m_j2kDecodeModule = pJ2kDecodeModule; |
80 | } | 72 | } |
81 | 73 | ||
74 | public LLClientView Client | ||
75 | { | ||
76 | get { return m_client; } | ||
77 | } | ||
78 | |||
79 | public AssetBase MissingImage | ||
80 | { | ||
81 | get { return m_missingImage; } | ||
82 | } | ||
83 | |||
82 | public void EnqueueReq(TextureRequestArgs newRequest) | 84 | public void EnqueueReq(TextureRequestArgs newRequest) |
83 | { | 85 | { |
84 | //newRequest is the properties of our new texture fetch request. | 86 | //newRequest is the properties of our new texture fetch request. |
@@ -88,234 +90,203 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
88 | //Make sure we're not shutting down.. | 90 | //Make sure we're not shutting down.. |
89 | if (!m_shuttingdown) | 91 | if (!m_shuttingdown) |
90 | { | 92 | { |
93 | J2KImage imgrequest; | ||
91 | 94 | ||
92 | //Do we already know about this UUID? | 95 | // Do a linear search for this texture download |
93 | if (m_imagestore.ContainsKey(newRequest.RequestedAssetID)) | 96 | lock (m_priorityQueue) |
94 | { | 97 | m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest); |
95 | //Check the packet sequence to make sure this isn't older than | ||
96 | //one we've already received | ||
97 | |||
98 | J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID]; | ||
99 | |||
100 | // This is the inbound request sequence number. We can ignore | ||
101 | // "old" ones. | ||
102 | 98 | ||
103 | if (newRequest.requestSequence > imgrequest.m_lastSequence) | 99 | if (imgrequest != null) |
100 | { | ||
101 | if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) | ||
104 | { | 102 | { |
103 | //m_log.Debug("[TEX]: (CAN) ID=" + newRequest.RequestedAssetID); | ||
105 | 104 | ||
106 | imgrequest.m_lastSequence = newRequest.requestSequence; | 105 | try |
107 | |||
108 | //Check the priority | ||
109 | |||
110 | double priority = imgrequest.m_requestedPriority; | ||
111 | if (priority != newRequest.Priority) | ||
112 | { | 106 | { |
113 | //Remove the old priority | 107 | lock (m_priorityQueue) |
114 | m_priorities.Remove(imgrequest.m_designatedPriorityKey); | 108 | m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle); |
115 | //Assign a new unique priority | ||
116 | imgrequest.m_requestedPriority = newRequest.Priority; | ||
117 | imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); | ||
118 | } | 109 | } |
110 | catch (Exception) { } | ||
111 | } | ||
112 | else | ||
113 | { | ||
114 | //m_log.DebugFormat("[TEX]: (UPD) ID={0}: D={1}, S={2}, P={3}", | ||
115 | // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); | ||
119 | 116 | ||
120 | //Update the requested discard level | 117 | //Check the packet sequence to make sure this isn't older than |
121 | imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; | 118 | //one we've already received |
119 | if (newRequest.requestSequence > imgrequest.m_lastSequence) | ||
120 | { | ||
121 | //Update the sequence number of the last RequestImage packet | ||
122 | imgrequest.m_lastSequence = newRequest.requestSequence; | ||
122 | 123 | ||
123 | //Update the requested packet number | 124 | //Update the requested discard level |
124 | imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; | 125 | imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; |
125 | 126 | ||
126 | //Check if this will create an outstanding texture request | 127 | //Update the requested packet number |
127 | bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel; | 128 | imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; |
128 | //Run an update | ||
129 | 129 | ||
130 | imgrequest.RunUpdate(); | 130 | //Update the requested priority |
131 | imgrequest.m_requestedPriority = newRequest.Priority; | ||
132 | try | ||
133 | { | ||
134 | lock (m_priorityQueue) | ||
135 | m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest); | ||
136 | } | ||
137 | catch (Exception) | ||
138 | { | ||
139 | imgrequest.m_priorityQueueHandle = null; | ||
140 | lock (m_priorityQueue) | ||
141 | m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); | ||
142 | } | ||
131 | 143 | ||
132 | if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) | 144 | //Run an update |
133 | { | 145 | imgrequest.RunUpdate(); |
134 | Interlocked.Increment(ref m_outstandingtextures); | ||
135 | } | 146 | } |
136 | } | 147 | } |
137 | } | 148 | } |
138 | else | 149 | else |
139 | { | 150 | { |
140 | J2KImage imgrequest = new J2KImage(this); | 151 | if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) |
141 | 152 | { | |
142 | //Assign our missing substitute | 153 | //m_log.DebugFormat("[TEX]: (IGN) ID={0}: D={1}, S={2}, P={3}", |
143 | imgrequest.m_MissingSubstitute = m_missingsubstitute; | 154 | // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); |
155 | } | ||
156 | else | ||
157 | { | ||
158 | //m_log.DebugFormat("[TEX]: (NEW) ID={0}: D={1}, S={2}, P={3}", | ||
159 | // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); | ||
144 | 160 | ||
145 | //Assign our decoder module | 161 | imgrequest = new J2KImage(this); |
146 | imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; | ||
147 | 162 | ||
148 | //Assign our asset cache module | 163 | //Assign our decoder module |
149 | imgrequest.m_assetCache = m_assetCache; | 164 | imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; |
150 | 165 | ||
151 | //Assign a priority based on our request | 166 | //Assign our asset cache module |
152 | imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); | 167 | imgrequest.m_assetCache = m_assetCache; |
153 | 168 | ||
154 | //Assign the requested discard level | 169 | //Assign the requested discard level |
155 | imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; | 170 | imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; |
156 | 171 | ||
157 | //Assign the requested packet number | 172 | //Assign the requested packet number |
158 | imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; | 173 | imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; |
159 | 174 | ||
160 | //Assign the requested priority | 175 | //Assign the requested priority |
161 | imgrequest.m_requestedPriority = newRequest.Priority; | 176 | imgrequest.m_requestedPriority = newRequest.Priority; |
162 | 177 | ||
163 | //Assign the asset uuid | 178 | //Assign the asset uuid |
164 | imgrequest.m_requestedUUID = newRequest.RequestedAssetID; | 179 | imgrequest.m_requestedUUID = newRequest.RequestedAssetID; |
165 | 180 | ||
166 | m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest); | 181 | //Assign the requested priority |
182 | imgrequest.m_requestedPriority = newRequest.Priority; | ||
167 | 183 | ||
168 | //Run an update | 184 | //Add this download to the priority queue |
169 | imgrequest.RunUpdate(); | 185 | lock (m_priorityQueue) |
186 | m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); | ||
170 | 187 | ||
188 | //Run an update | ||
189 | imgrequest.RunUpdate(); | ||
190 | } | ||
171 | } | 191 | } |
172 | } | 192 | } |
173 | } | 193 | } |
174 | 194 | ||
175 | private double AssignPriority(UUID pAssetID, double pPriority) | 195 | public bool ProcessImageQueue(int count, int maxpack) |
176 | { | 196 | { |
177 | 197 | lock (this) | |
178 | //First, find out if we can just assign directly | ||
179 | if (m_priorityresolver.ContainsKey((int)pPriority) == false) | ||
180 | { | 198 | { |
181 | m_priorities.Add((double)((int)pPriority), pAssetID); | 199 | //count is the number of textures we want to process in one go. |
182 | m_priorityresolver.Add((int)pPriority, 0); | 200 | //As part of this class re-write, that number will probably rise |
183 | return (double)((int)pPriority); | 201 | //since we're processing in a more efficient manner. |
184 | } | ||
185 | else | ||
186 | { | ||
187 | //Use the hash lookup goodness of a secondary dictionary to find a free slot | ||
188 | double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1)); | ||
189 | m_priorities[mFreePriority] = pAssetID; | ||
190 | m_priorityresolver[(int)pPriority]++; | ||
191 | return mFreePriority; | ||
192 | } | ||
193 | 202 | ||
203 | // this can happen during Close() | ||
204 | if (m_client == null) | ||
205 | return false; | ||
194 | 206 | ||
207 | int numCollected = 0; | ||
195 | 208 | ||
196 | } | 209 | //Calculate our threshold |
210 | int threshold; | ||
211 | if (m_lastloopprocessed == 0) | ||
212 | { | ||
213 | if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) | ||
214 | return false; | ||
215 | //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below | ||
216 | threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; | ||
217 | m_lastloopprocessed = DateTime.Now.Ticks; | ||
218 | } | ||
219 | else | ||
220 | { | ||
221 | double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; | ||
222 | throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; | ||
197 | 223 | ||
198 | public bool ProcessImageQueue(int count, int maxpack) | 224 | //Average of 1000 bytes per packet |
199 | { | 225 | throttleseconds = throttleseconds / 1000; |
226 | |||
227 | //Safe-zone multiplier of 2.0 | ||
228 | threshold = (int)(throttleseconds * 2.0); | ||
229 | m_lastloopprocessed = DateTime.Now.Ticks; | ||
230 | |||
231 | } | ||
232 | |||
233 | if (m_client.PacketHandler == null) | ||
234 | return false; | ||
200 | 235 | ||
201 | // this can happen during Close() | 236 | if (m_client.PacketHandler.PacketQueue == null) |
202 | if (m_client == null) | 237 | return false; |
203 | return false; | 238 | |
204 | 239 | if (threshold < 10) | |
205 | //Count is the number of textures we want to process in one go. | 240 | threshold = 10; |
206 | //As part of this class re-write, that number will probably rise | 241 | |
207 | //since we're processing in a more efficient manner. | 242 | //Uncomment this to see what the texture stack is doing |
208 | 243 | //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); | |
209 | int numCollected = 0; | 244 | if (true) //m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold) |
210 | |||
211 | //Calculate our threshold | ||
212 | int threshold; | ||
213 | if (m_lastloopprocessed == 0) | ||
214 | { | ||
215 | if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) | ||
216 | return false; | ||
217 | //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below | ||
218 | threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; | ||
219 | m_lastloopprocessed = DateTime.Now.Ticks; | ||
220 | } | ||
221 | else | ||
222 | { | ||
223 | double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; | ||
224 | throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; | ||
225 | |||
226 | //Average of 1000 bytes per packet | ||
227 | throttleseconds = throttleseconds / 1000; | ||
228 | |||
229 | //Safe-zone multiplier of 2.0 | ||
230 | threshold = (int)(throttleseconds * 2.0); | ||
231 | m_lastloopprocessed = DateTime.Now.Ticks; | ||
232 | |||
233 | } | ||
234 | |||
235 | if (threshold < 10) | ||
236 | { | ||
237 | threshold = 10; | ||
238 | } | ||
239 | |||
240 | if (m_client.PacketHandler == null) | ||
241 | return false; | ||
242 | |||
243 | if (m_client.PacketHandler.PacketQueue == null) | ||
244 | return false; | ||
245 | |||
246 | //First of all make sure our packet queue isn't above our threshold | ||
247 | |||
248 | //Uncomment this to see what the texture stack is doing | ||
249 | //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); | ||
250 | if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold && m_outstandingtextures > 0) | ||
251 | { | ||
252 | bool justreset = false; | ||
253 | |||
254 | for (int x = m_priorities.Count - 1; x > -1; x--) | ||
255 | { | 245 | { |
256 | 246 | while (m_priorityQueue.Count > 0) | |
257 | J2KImage imagereq = m_imagestore[m_priorities.Values[x]]; | ||
258 | if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel) | ||
259 | { | 247 | { |
260 | numCollected++; | 248 | J2KImage imagereq = null; |
261 | //SendPackets will send up to ten packets per cycle | 249 | lock (m_priorityQueue) |
262 | if (imagereq.SendPackets(m_client, maxpack)) | 250 | imagereq = m_priorityQueue.FindMax(); |
251 | |||
252 | if (imagereq.m_decoded == true) | ||
263 | { | 253 | { |
264 | //Send complete | 254 | // we need to test this here now that we are dropping assets |
265 | if (!imagereq.m_completedSendAtCurrentDiscardLevel) | 255 | if (!imagereq.m_hasasset) |
266 | { | 256 | { |
267 | imagereq.m_completedSendAtCurrentDiscardLevel = true; | 257 | m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID); |
268 | Interlocked.Decrement(ref m_outstandingtextures); | 258 | imagereq.RunUpdate(); |
269 | //Re-assign priority to bottom | 259 | continue; |
270 | //Remove the old priority | 260 | } |
271 | m_priorities.Remove(imagereq.m_designatedPriorityKey); | 261 | |
272 | int lowest; | 262 | ++numCollected; |
273 | if (m_priorities.Count > 0) | 263 | |
274 | { | 264 | //SendPackets will send up to ten packets per cycle |
275 | lowest = (int)m_priorities.Keys[0]; | 265 | if (imagereq.SendPackets(m_client, maxpack)) |
276 | lowest--; | 266 | { |
277 | } | 267 | // Send complete. Destroy any knowledge of this transfer |
278 | else | 268 | try |
279 | { | 269 | { |
280 | lowest = -10000; | 270 | lock (m_priorityQueue) |
281 | } | 271 | m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); |
282 | m_priorities.Add((double)lowest, imagereq.m_requestedUUID); | ||
283 | imagereq.m_designatedPriorityKey = (double)lowest; | ||
284 | if (m_priorityresolver.ContainsKey((int)lowest)) | ||
285 | { | ||
286 | m_priorityresolver[(int)lowest]++; | ||
287 | } | ||
288 | else | ||
289 | { | ||
290 | m_priorityresolver.Add((int)lowest, 0); | ||
291 | } | 272 | } |
273 | catch (Exception) { } | ||
292 | } | 274 | } |
293 | } | 275 | } |
276 | |||
294 | if (numCollected == count) | 277 | if (numCollected == count) |
295 | { | ||
296 | break; | 278 | break; |
297 | } | ||
298 | } | ||
299 | if (numCollected == count || m_outstandingtextures == 0) | ||
300 | break; | ||
301 | if (numCollected % m_outstandingtextures == 0 && !justreset) | ||
302 | { | ||
303 | //We've gotten as much as we can from the stack, | ||
304 | //reset to the top so that we can send MOAR DATA (nomnomnom)! | ||
305 | x = m_priorities.Count - 1; | ||
306 | |||
307 | justreset = true; //prevents us from getting stuck in a loop | ||
308 | } | 279 | } |
309 | } | 280 | } |
310 | } | ||
311 | 281 | ||
312 | return m_outstandingtextures != 0; | 282 | return m_priorityQueue.Count > 0; |
283 | } | ||
313 | } | 284 | } |
314 | 285 | ||
315 | //Faux destructor | 286 | //Faux destructor |
316 | public void Close() | 287 | public void Close() |
317 | { | 288 | { |
318 | 289 | ||
319 | m_shuttingdown = true; | 290 | m_shuttingdown = true; |
320 | m_j2kDecodeModule = null; | 291 | m_j2kDecodeModule = null; |
321 | m_assetCache = null; | 292 | m_assetCache = null; |