aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorTeravus Ovares2009-04-10 08:30:21 +0000
committerTeravus Ovares2009-04-10 08:30:21 +0000
commit515bf6d7dcce10d5e32db489c0d6247bd3b2a615 (patch)
tree84a6d08e761a2f7d5758b44f2cc71dc98eaec5d7 /OpenSim
parentHandle ObjectSpin* packets to spin physical prims on Ctrl+Shift+Drag (diff)
downloadopensim-SC_OLD-515bf6d7dcce10d5e32db489c0d6247bd3b2a615.zip
opensim-SC_OLD-515bf6d7dcce10d5e32db489c0d6247bd3b2a615.tar.gz
opensim-SC_OLD-515bf6d7dcce10d5e32db489c0d6247bd3b2a615.tar.bz2
opensim-SC_OLD-515bf6d7dcce10d5e32db489c0d6247bd3b2a615.tar.xz
* Patch from RemedyTomm Mantis 3440
* Revamps the server side texture pipeline * Textures should load faster, get clogged less, and be less blurry * Minor tweak to ensure the outgoing texture throttle stays private. * Fixes mantis 3440
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Framework/TextureRequestArgs.cs7
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs7
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs917
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs14
-rw-r--r--OpenSim/Region/UserStatistics/WebStatsModule.cs15
5 files changed, 449 insertions, 511 deletions
diff --git a/OpenSim/Framework/TextureRequestArgs.cs b/OpenSim/Framework/TextureRequestArgs.cs
index 4dc6dca..4668be7 100644
--- a/OpenSim/Framework/TextureRequestArgs.cs
+++ b/OpenSim/Framework/TextureRequestArgs.cs
@@ -36,6 +36,7 @@ namespace OpenSim.Framework
36 private uint m_packetNumber; 36 private uint m_packetNumber;
37 private float m_priority; 37 private float m_priority;
38 private int m_requestType; 38 private int m_requestType;
39 private uint m_requestsequence;
39 protected UUID m_requestedAssetID; 40 protected UUID m_requestedAssetID;
40 41
41 public float Priority 42 public float Priority
@@ -53,6 +54,12 @@ namespace OpenSim.Framework
53 set { m_packetNumber = value; } 54 set { m_packetNumber = value; }
54 } 55 }
55 56
57 public uint requestSequence
58 {
59 get { return m_requestsequence; }
60 set { m_requestsequence = value; }
61 }
62
56 /// <summary> 63 /// <summary>
57 /// 64 ///
58 /// </summary> 65 /// </summary>
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 239c4b3..2c8474c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -4430,7 +4430,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4430 4430
4431 // in the end, we dereference this, so we have to check if it's null 4431 // in the end, we dereference this, so we have to check if it's null
4432 if (m_imageManager != null) 4432 if (m_imageManager != null)
4433 m_imageManager.ProcessImageQueue(3); 4433 m_imageManager.ProcessImageQueue(5);
4434 return; 4434 return;
4435 } 4435 }
4436 4436
@@ -6041,7 +6041,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6041 case PacketType.RequestImage: 6041 case PacketType.RequestImage:
6042 RequestImagePacket imageRequest = (RequestImagePacket)Pack; 6042 RequestImagePacket imageRequest = (RequestImagePacket)Pack;
6043 //m_log.Debug("image request: " + Pack.ToString()); 6043 //m_log.Debug("image request: " + Pack.ToString());
6044 6044
6045 #region Packet Session and User Check 6045 #region Packet Session and User Check
6046 if (m_checkPackets) 6046 if (m_checkPackets)
6047 { 6047 {
@@ -6062,6 +6062,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6062 args.DiscardLevel = imageRequest.RequestImage[i].DiscardLevel; 6062 args.DiscardLevel = imageRequest.RequestImage[i].DiscardLevel;
6063 args.PacketNumber = imageRequest.RequestImage[i].Packet; 6063 args.PacketNumber = imageRequest.RequestImage[i].Packet;
6064 args.Priority = imageRequest.RequestImage[i].DownloadPriority; 6064 args.Priority = imageRequest.RequestImage[i].DownloadPriority;
6065 args.requestSequence = imageRequest.Header.Sequence;
6065 6066
6066 //handlerTextureRequest = OnRequestTexture; 6067 //handlerTextureRequest = OnRequestTexture;
6067 6068
@@ -9114,7 +9115,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
9114 9115
9115 // in the end, we dereference this, so we have to check if it's null 9116 // in the end, we dereference this, so we have to check if it's null
9116 if (m_imageManager != null ) 9117 if (m_imageManager != null )
9117 m_imageManager.ProcessImageQueue(3); 9118 m_imageManager.ProcessImageQueue(10);
9118 PacketPool.Instance.ReturnPacket(Pack); 9119 PacketPool.Instance.ReturnPacket(Pack);
9119 } 9120 }
9120 9121
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index 0fb27d4..6bfab90 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -27,7 +27,6 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using C5;
31using OpenMetaverse; 30using OpenMetaverse;
32using OpenMetaverse.Imaging; 31using OpenMetaverse.Imaging;
33using OpenSim.Framework; 32using OpenSim.Framework;
@@ -38,642 +37,546 @@ using System.Reflection;
38namespace OpenSim.Region.ClientStack.LindenUDP 37namespace OpenSim.Region.ClientStack.LindenUDP
39{ 38{
40 39
41 /// <summary>
42 /// Client image priority + discardlevel sender/manager
43 /// </summary>
44 public class LLImageManager 40 public class LLImageManager
45 { 41 {
42
43 //Public interfaces:
44 //Constructor - (LLClientView, IAssetCache, IJ2KDecoder);
45 //void EnqueueReq - (TextureRequestArgs)
46 //ProcessImageQueue
47 //Close
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49 private bool m_shuttingdown = false;
50
51 private LLClientView m_client; //Client we're assigned to
52 private IAssetCache m_assetCache; //Asset Cache
53 private IJ2KDecoder m_j2kDecodeModule; //Our J2K module
46 54
47 /// <summary> 55 private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes
48 /// Priority Queue for images. Contains lots of data 56 private Dictionary<UUID,J2KImage> m_imagestore; // Our main image storage dictionary
49 /// </summary> 57 private SortedList<double,UUID> m_priorities; // For fast image lookup based on priority
50 private readonly IPriorityQueue<Prio<J2KImage>> pq = new IntervalHeap<Prio<J2KImage>>(); 58 private Dictionary<int, int> m_priorityresolver; //Enabling super fast assignment of images with the same priorities
51 59
52 /// <summary> 60 private const double doubleMinimum = .0000001;
53 /// Dictionary of PriorityQueue handles by AssetId 61 //Constructor
54 /// </summary>
55 private readonly Dictionary<UUID, IPriorityQueueHandle<Prio<J2KImage>>> PQHandles =
56 new Dictionary<UUID, IPriorityQueueHandle<Prio<J2KImage>>>();
57
58 private LLClientView m_client;
59 private readonly IAssetCache m_assetCache;
60 private bool m_shuttingdown = false;
61 private readonly IJ2KDecoder m_j2kDecodeModule;
62
63 private readonly AssetBase MissingSubstitute;
64
65 /// <summary>
66 /// Client image priority + discardlevel sender/manager
67 /// </summary>
68 /// <param name="client">LLClientView of client</param>
69 /// <param name="pAssetCache">The Asset retrieval system</param>
70 /// <param name="pJ2kDecodeModule">The Jpeg2000 Decoder</param>
71 public LLImageManager(LLClientView client, IAssetCache pAssetCache, IJ2KDecoder pJ2kDecodeModule) 62 public LLImageManager(LLClientView client, IAssetCache pAssetCache, IJ2KDecoder pJ2kDecodeModule)
72 { 63 {
64
65 m_imagestore = new Dictionary<UUID,J2KImage>();
66 m_priorities = new SortedList<double,UUID>();
67 m_priorityresolver = new Dictionary<int, int>();
73 m_client = client; 68 m_client = client;
74 m_assetCache = pAssetCache; 69 m_assetCache = pAssetCache;
75 if (pAssetCache != null) 70 if (pAssetCache != null)
76 MissingSubstitute = pAssetCache.GetAsset(UUID.Parse("5748decc-f629-461c-9a36-a35a221fe21f"), true); 71 m_missingsubstitute = pAssetCache.GetAsset(UUID.Parse("5748decc-f629-461c-9a36-a35a221fe21f"), true);
77 m_j2kDecodeModule = pJ2kDecodeModule; 72 m_j2kDecodeModule = pJ2kDecodeModule;
78 } 73 }
79 74
80 /// <summary> 75 public void EnqueueReq(TextureRequestArgs newRequest)
81 /// Enqueues a texture request
82 /// </summary>
83 /// <param name="req">Request from the client to get a texture</param>
84 public void EnqueueReq(TextureRequestArgs req)
85 { 76 {
86 if (m_shuttingdown) 77 //newRequest is the properties of our new texture fetch request.
87 return; 78 //Basically, here is where we queue up "new" requests..
79 // .. or modify existing requests to suit.
88 80
89 //if (req.RequestType == 1) // avatar body texture! 81 //Make sure we're not shutting down..
90 // return; 82 if (!m_shuttingdown)
83 {
91 84
92 AddQueueItem(req.RequestedAssetID, (int)req.Priority + 100000); 85 //Do we already know about this UUID?
93 //if (pq[PQHandles[req.RequestedAssetID]].data.Missing) 86 if (m_imagestore.ContainsKey(newRequest.RequestedAssetID))
94 //{ 87 {
95 // pq[PQHandles[req.RequestedAssetID]] -= 900000; 88 //Check the packet sequence to make sure this isn't older than
96 //} 89 //one we've already received
97 //
98 //if (pq[PQHandles[req.RequestedAssetID]].data.HasData && pq[PQHandles[req.RequestedAssetID]].data.Layers.Length > 0)
99 //{
100 90
101 //} 91 J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID];
102 92
103 pq[PQHandles[req.RequestedAssetID]].data.requestedUUID = req.RequestedAssetID; 93 //if (newRequest.requestSequence > imgrequest.m_lastSequence)
104 pq[PQHandles[req.RequestedAssetID]].data.Priority = (int)req.Priority; 94 //{
95 imgrequest.m_lastSequence = newRequest.requestSequence;
105 96
106 lock (pq[PQHandles[req.RequestedAssetID]].data) 97 //First of all, is this being killed?
107 pq[PQHandles[req.RequestedAssetID]].data.Update(req.DiscardLevel, (int)req.PacketNumber); 98 if (newRequest.Priority == 0.0f && newRequest.DiscardLevel == -1)
108 } 99 {
100 //Remove the old priority
101 m_priorities.Remove(imgrequest.m_designatedPriorityKey);
102 m_imagestore.Remove(imgrequest.m_requestedUUID);
103 imgrequest = null;
104 }
105 else
106 {
109 107
110 /// <summary>
111 /// Callback for the asset system
112 /// </summary>
113 /// <param name="assetID">UUID of the asset that we have received</param>
114 /// <param name="asset">AssetBase of the asset that we've received</param>
115 public void AssetDataCallback(UUID assetID, AssetBase asset)
116 {
117 if (m_shuttingdown)
118 return;
119 108
120 //m_log.Debug("AssetCallback for assetId" + assetID); 109 //Check the priority
110 double priority = imgrequest.m_requestedPriority;
111 if (priority != newRequest.Priority)
112 {
113 //Remove the old priority
114 m_priorities.Remove(imgrequest.m_designatedPriorityKey);
115 //Assign a new unique priority
116 imgrequest.m_requestedPriority = newRequest.Priority;
117 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
118 }
121 119
122 if (asset == null || asset.Data == null) 120 //Update the requested discard level
123 { 121 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
124 lock (pq) 122
125 { 123 //Update the requested packet number
126 //pq[PQHandles[assetID]].data.Missing = true; 124 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
127 pq[PQHandles[assetID]].data.asset = MissingSubstitute; 125
128 pq[PQHandles[assetID]].data.Missing = false; 126 //Run an update
127 imgrequest.RunUpdate();
128 }
129 //}
129 } 130 }
130 } 131 else
131 //else 132 {
133 J2KImage imgrequest = new J2KImage();
132 134
133 pq[PQHandles[assetID]].data.asset = asset; 135 //Assign our missing substitute
136 imgrequest.m_MissingSubstitute = m_missingsubstitute;
134 137
135 //lock (pq[PQHandles[assetID]].data) 138 //Assign our decoder module
136 pq[PQHandles[assetID]].data.Update((int)pq[PQHandles[assetID]].data.Priority, pq[PQHandles[assetID]].data.CurrentPacket); 139 imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
137 }
138 140
139 /// <summary> 141 //Assign our asset cache module
140 /// Processes the image queue. Pops count elements off and processes them 142 imgrequest.m_assetCache = m_assetCache;
141 /// </summary>
142 /// <param name="count">number of images to peek off the queue</param>
143 public void ProcessImageQueue(int count)
144 {
145 if (m_shuttingdown)
146 return;
147 143
148 IPriorityQueueHandle<Prio<J2KImage>> h = null; 144 //Assign a priority based on our request
149 for (int j = 0; j < count; j++) 145 imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
150 {
151 lock (pq)
152 {
153 if (!pq.IsEmpty)
154 {
155 //peek off the top
156 Prio<J2KImage> process = pq.FindMax(out h);
157 146
158 // Do we have the Asset Data? 147 //Assign the requested discard level
159 if (!process.data.HasData) 148 imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
160 {
161 // Did we request the asset data?
162 if (!process.data.dataRequested)
163 {
164 m_assetCache.GetAsset(process.data.requestedUUID, AssetDataCallback, true);
165 pq[h].data.dataRequested = true;
166 }
167 149
168 // Is the asset missing? 150 //Assign the requested packet number
169 if (process.data.Missing) 151 imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
170 {
171 152
172 //m_client.sendtextur 153 //Assign the requested priority
173 pq[h] -= 90000; 154 imgrequest.m_requestedPriority = newRequest.Priority;
174 /*
175 {
176 OpenMetaverse.Packets.ImageNotInDatabasePacket imdback =
177 new OpenMetaverse.Packets.ImageNotInDatabasePacket();
178 imdback.ImageID =
179 new OpenMetaverse.Packets.ImageNotInDatabasePacket.ImageIDBlock();
180 imdback.ImageID.ID = process.data.requestedUUID;
181 m_client.OutPacket(imdback, ThrottleOutPacketType.Texture);
182 }
183 */
184
185 // Substitute a blank image
186 process.data.asset = MissingSubstitute;
187 process.data.Missing = false;
188
189 // If the priority is less then -4billion, the client has forgotten about it.
190 if (pq[h] < -400000000)
191 {
192 RemoveItemFromQueue(pq[h].data.requestedUUID);
193 continue;
194 }
195 }
196 // Lower the priority to give the next image a chance
197 pq[h] -= 100000;
198 }
199 else if (process.data.HasData)
200 {
201 // okay, we've got the data
202 lock (process.data)
203 {
204 if (!process.data.J2KDecode && !process.data.J2KDecodeWaiting)
205 {
206 process.data.J2KDecodeWaiting = true;
207
208 // Do we have a jpeg decoder?
209 if (m_j2kDecodeModule != null)
210 {
211 // Send it off to the jpeg decoder
212 m_j2kDecodeModule.decode(process.data.requestedUUID, process.data.Data,
213 j2kDecodedCallback);
214 }
215 else
216 {
217 // no module, no layers, full resolution only
218 j2kDecodedCallback(process.data.AssetId, new OpenJPEG.J2KLayerInfo[0]);
219 }
220 } // Are we waiting?
221 else if (!process.data.J2KDecodeWaiting)
222 {
223 // Send more data at a time for higher discard levels
224 bool done = false;
225 for (int i = 0; i < (2*(process.data.DiscardLevel) + 1)*2; i++)
226 if (!process.data.SendPacket(m_client))
227 {
228 done = true;
229 pq[h] -= (500000*i);
230 break;
231 }
232 if (!done)
233 {
234 for (int i = 0; i < (2 * (5- process.data.DiscardLevel) + 1) * 2; i++)
235 if (!process.data.SendPacket(m_client))
236 {
237 done = true;
238 pq[h] -= (500000 * i);
239 break;
240 }
241 }
242 }
243 // If the priority is less then -4 billion, the client has forgotten about it, pop it off
244 if (pq[h] < -400000000)
245 {
246 RemoveItemFromQueue(pq[h].data.requestedUUID);
247 continue;
248 }
249 }
250 155
251 //pq[h] = process; 156 //Assign the asset uuid
252 } 157 imgrequest.m_requestedUUID = newRequest.RequestedAssetID;
253 158
254 // uncomment the following line to see the upper most asset and the priority 159 m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest);
255 //m_log.Debug(process.ToString()); 160
161 //Run an update
162 imgrequest.RunUpdate();
256 163
257 // Lower priority to give the next image a chance to bubble up
258 pq[h] -= 50000;
259 }
260 } 164 }
261 } 165 }
262 } 166 }
263 167
264 /// <summary> 168 private double AssignPriority(UUID pAssetID, double pPriority)
265 /// Callback for when the image has been decoded
266 /// </summary>
267 /// <param name="AssetId">The UUID of the Asset</param>
268 /// <param name="layers">The Jpeg2000 discard level Layer start and end byte offsets Array. 0 elements for failed or no decoder</param>
269 public void j2kDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
270 { 169 {
271 // are we shutting down? if so, end. 170
272 if (m_shuttingdown) 171 //First, find out if we can just assign directly
273 return; 172 if (m_priorityresolver.ContainsKey((int)pPriority) == false)
274
275 lock (PQHandles)
276 { 173 {
277 // Update our asset data 174 m_priorities.Add((double)((int)pPriority), pAssetID);
278 if (PQHandles.ContainsKey(AssetId)) 175 m_priorityresolver.Add((int)pPriority, 0);
279 { 176 return (double)((int)pPriority);
280 pq[PQHandles[AssetId]].data.Layers = layers;
281 pq[PQHandles[AssetId]].data.J2KDecode = true;
282 pq[PQHandles[AssetId]].data.J2KDecodeWaiting = false;
283 //lock (pq[PQHandles[AssetId]].data)
284 pq[PQHandles[AssetId]].data.Update((int)pq[PQHandles[AssetId]].data.Priority, (int)pq[PQHandles[AssetId]].data.CurrentPacket);
285
286 // Send the first packet
287 pq[PQHandles[AssetId]].data.SendPacket(m_client);
288 }
289 } 177 }
290 } 178 else
291
292 /// <summary>
293 /// This image has had a good life. It's now expired. Remove it off the queue
294 /// </summary>
295 /// <param name="AssetId">UUID of asset to remove off the queue</param>
296 private void RemoveItemFromQueue(UUID AssetId)
297 {
298 lock (PQHandles)
299 { 179 {
300 if (PQHandles.ContainsKey(AssetId)) 180 //Use the hash lookup goodness of a secondary dictionary to find a free slot
301 { 181 double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1));
302 IPriorityQueueHandle<Prio<J2KImage>> h = PQHandles[AssetId]; 182 m_priorities[mFreePriority] = pAssetID;
303 PQHandles.Remove(AssetId); 183 m_priorityresolver[(int)pPriority]++;
304 pq.Delete(h); 184 return mFreePriority;
305 }
306 } 185 }
307 }
308 186
309 187
310 /// <summary>
311 /// Adds an image to the queue and update priority
312 /// if the item is already in the queue, just update the priority
313 /// </summary>
314 /// <param name="AssetId">UUID of the asset</param>
315 /// <param name="priority">Priority to set</param>
316 private void AddQueueItem(UUID AssetId, int priority)
317 {
318 IPriorityQueueHandle<Prio<J2KImage>> h = null;
319 188
320 lock (PQHandles) 189 }
190
191 public void ProcessImageQueue(int count)
192 {
193
194 //Count is the number of textures we want to process in one go.
195 //As part of this class re-write, that number will probably rise
196 //since we're processing in a more efficient manner.
197
198 int numCollected = 0;
199 //First of all make sure our packet queue isn't above our threshold
200 if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < 200)
321 { 201 {
322 if (PQHandles.ContainsKey(AssetId)) 202
203 for (int x = m_priorities.Count - 1; x > -1; x--)
323 { 204 {
324 h = PQHandles[AssetId]; 205
325 pq[h] = pq[h].SetPriority(priority); 206 J2KImage imagereq = m_imagestore[m_priorities.Values[x]];
207 if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel)
208 {
326 209
327 } 210 numCollected++;
328 else 211 //SendPackets will send up to ten packets per cycle
329 { 212 //m_log.Debug("Processing packet with priority of " + imagereq.m_designatedPriorityKey.ToString());
330 J2KImage newreq = new J2KImage(); 213 if (imagereq.SendPackets(m_client))
331 newreq.requestedUUID = AssetId; 214 {
332 pq.Add(ref h, new Prio<J2KImage>(newreq, priority)); 215 //Send complete
333 PQHandles.Add(AssetId, h); 216 imagereq.m_completedSendAtCurrentDiscardLevel = true;
217 //Re-assign priority to bottom
218 //Remove the old priority
219 m_priorities.Remove(imagereq.m_designatedPriorityKey);
220 int lowest;
221 if (m_priorities.Count > 0)
222 {
223 lowest = (int)m_priorities.Keys[0];
224 lowest--;
225 }
226 else
227 {
228 lowest = -10000;
229 }
230 m_priorities.Add((double)lowest, imagereq.m_requestedUUID);
231 imagereq.m_designatedPriorityKey = (double)lowest;
232 if (m_priorityresolver.ContainsKey((int)lowest))
233 {
234 m_priorityresolver[(int)lowest]++;
235 }
236 else
237 {
238 m_priorityresolver.Add((int)lowest, 0);
239 }
240 }
241 //m_log.Debug("...now has priority of " + imagereq.m_designatedPriorityKey.ToString());
242 if (numCollected == count)
243 {
244 break;
245 }
246 }
334 } 247 }
335 } 248 }
249
250
251
336 } 252 }
337 253
338 /// <summary> 254 //Faux destructor
339 /// Okay, we're ending. Clean up on isle 9
340 /// </summary>
341 public void Close() 255 public void Close()
342 { 256 {
257
343 m_shuttingdown = true; 258 m_shuttingdown = true;
344 259 m_j2kDecodeModule = null;
345 lock (pq) 260 m_assetCache = null;
346 {
347 while (!pq.IsEmpty)
348 {
349 pq.DeleteMin();
350 }
351 }
352
353 lock (PQHandles)
354 PQHandles.Clear();
355 m_client = null; 261 m_client = null;
356 } 262 }
263
264
357 } 265 }
358 266
359 /// <summary> 267 /*
360 /// Image Data for this send 268 *
361 /// Encapsulates the image sending data and method 269 * J2KImage
362 /// </summary> 270 *
271 * We use this class to store image data and associated request data and attributes
272 *
273 *
274 *
275 */
276
363 public class J2KImage 277 public class J2KImage
364 { 278 {
365 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 279 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
366 private AssetBase m_asset_ref = null; 280 public double m_designatedPriorityKey;
367 public volatile int LastPacketNum = 0; 281 public double m_requestedPriority = 0.0d;
368 public volatile int DiscardLimit = 0; 282 public uint m_lastSequence = 0;
369 public volatile bool dataRequested = false; 283 public uint m_requestedPacketNumber;
284 public sbyte m_requestedDiscardLevel;
285 public UUID m_requestedUUID;
286 public IJ2KDecoder m_j2kDecodeModule;
287 public IAssetCache m_assetCache;
370 public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0]; 288 public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0];
371 289 public AssetBase m_MissingSubstitute = null;
372 public const int FIRST_IMAGE_PACKET_SIZE = 600; 290 public bool m_decoded = false;
373 public const int IMAGE_PACKET_SIZE = 1000; 291 public bool m_completedSendAtCurrentDiscardLevel;
374 292
375 public volatile int DiscardLevel; 293 private sbyte m_discardLevel=-1;
376 public float Priority; 294 private uint m_packetNumber;
377 public volatile int CurrentPacket = 1; 295 private bool m_decoderequested = false;
378 public volatile int StopPacket; 296 private bool m_hasasset = false;
379 public bool Missing = false; 297 private bool m_asset_requested = false;
380 public bool J2KDecode = false; 298 private bool m_sentinfo = false;
381 public bool J2KDecodeWaiting = false; 299 private uint m_stopPacket = 0;
382 300 private const int cImagePacketSize = 1000;
383 private volatile bool sendFirstPacket = true; 301 private const int cFirstPacketSize = 600;
384 302 private AssetBase m_asset = null;
385 // Having this *AND* the AssetId allows us to remap asset data to AssetIds as necessary. 303
386 public UUID requestedUUID = UUID.Zero; 304
387 305 public uint m_pPacketNumber
388 public J2KImage(AssetBase asset)
389 { 306 {
390 m_asset_ref = asset; 307 get { return m_packetNumber; }
391 } 308 }
392 309 public uint m_pStopPacketNumber
393 public J2KImage()
394 { 310 {
395 311 get { return m_stopPacket; }
396 } 312 }
397 313
398 public AssetBase asset 314 public byte[] Data
399 { 315 {
400 set { m_asset_ref = value; } 316 get { return m_asset.Data; }
401 } 317 }
402 318
403 // We make the asset a reference so that we don't duplicate the byte[] 319 public ushort TexturePacketCount()
404 // it's read only anyway, so no worries here
405 // we want to avoid duplicating the byte[] for the images at all costs to avoid memory bloat! :)
406
407 /// <summary>
408 /// ID of the AssetBase
409 /// </summary>
410 public UUID AssetId
411 { 320 {
412 get { return m_asset_ref.FullID; } 321 if (!m_decoded)
322 return 0;
323 return (ushort)(((m_asset.Data.Length - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1);
413 } 324 }
414 325
415 /// <summary> 326 public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
416 /// Asset Data
417 /// </summary>
418 public byte[] Data
419 { 327 {
420 get { return m_asset_ref.Data; } 328 Layers = layers;
329 m_decoded = true;
330 RunUpdate();
421 } 331 }
422 332
423 /// <summary> 333 public void AssetDataCallback(UUID AssetID, AssetBase asset)
424 /// Returns true if we have the asset
425 /// </summary>
426 public bool HasData
427 { 334 {
428 get { return !(m_asset_ref == null); } 335 m_hasasset = true;
336 if (asset == null || asset.Data == null)
337 {
338 m_asset = m_MissingSubstitute;
339 }
340 else
341 {
342 m_asset = asset;
343 }
344 RunUpdate();
429 } 345 }
430 346
431 /// <summary> 347 private int GetPacketForBytePosition(int bytePosition)
432 /// Called from the PriorityQueue handle .ToString(). Prints data on this asset
433 /// </summary>
434 /// <returns></returns>
435 public override string ToString()
436 { 348 {
437 return string.Format("ID:{0}, RD:{1}, CP:{2}", requestedUUID, HasData, CurrentPacket); 349 return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1;
438 } 350 }
439 351 public int LastPacketSize()
440 /// <summary>
441 /// Returns the total number of packets needed to transfer this texture,
442 /// including the first packet of size FIRST_IMAGE_PACKET_SIZE
443 /// </summary>
444 /// <returns>Total number of packets needed to transfer this texture</returns>
445 public int TexturePacketCount()
446 { 352 {
447 if (!HasData) 353 if (m_packetNumber == 1)
448 return 0; 354 return m_asset.Data.Length;
449 return ((m_asset_ref.Data.Length - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1; 355 return (m_asset.Data.Length - cFirstPacketSize) % cImagePacketSize;
450 } 356 }
451 357
452 /// <summary>
453 /// Returns the current byte offset for this transfer, calculated from
454 /// the CurrentPacket
455 /// </summary>
456 /// <returns>Current byte offset for this transfer</returns>
457 public int CurrentBytePosition() 358 public int CurrentBytePosition()
458 { 359 {
459 if (CurrentPacket == 0) 360 if (m_packetNumber == 0)
460 return 0; 361 return 0;
461 if (CurrentPacket == 1) 362 if (m_packetNumber == 1)
462 return FIRST_IMAGE_PACKET_SIZE; 363 return cFirstPacketSize;
463 364
464 int result = FIRST_IMAGE_PACKET_SIZE + (CurrentPacket - 2) * IMAGE_PACKET_SIZE; 365 int result = cFirstPacketSize + ((int)m_packetNumber - 2) * cImagePacketSize;
465 if (result < 0) 366 if (result < 0)
466 { 367 {
467 result = FIRST_IMAGE_PACKET_SIZE; 368 result = cFirstPacketSize;
468 } 369 }
469 return result; 370 return result;
470 } 371 }
471 372 public bool SendFirstPacket(LLClientView client)
472 /// <summary>
473 /// Returns the size, in bytes, of the last packet. This will be somewhere
474 /// between 1 and IMAGE_PACKET_SIZE bytes
475 /// </summary>
476 /// <returns>Size of the last packet in the transfer</returns>
477 public int LastPacketSize()
478 {
479 if (CurrentPacket == 1)
480 return m_asset_ref.Data.Length;
481 return (m_asset_ref.Data.Length - FIRST_IMAGE_PACKET_SIZE) % IMAGE_PACKET_SIZE; // m_asset_ref.Data.Length - (FIRST_IMAGE_PACKET_SIZE + ((TexturePacketCount() - 1) * IMAGE_PACKET_SIZE));
482 }
483
484 /// <summary>
485 /// Find the packet number that contains a given byte position
486 /// </summary>
487 /// <param name="bytePosition">Byte position</param>
488 /// <returns>Packet number that contains the given byte position</returns>
489 int GetPacketForBytePosition(int bytePosition)
490 {
491 return ((bytePosition - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
492 }
493
494 /// <summary>
495 /// Updates the Image sending limits based on the discard
496 /// If we don't have any Layers, Send the full texture
497 /// </summary>
498 /// <param name="discardLevel">jpeg2000 discard level. 5-0</param>
499 /// <param name="packet">Which packet to start from</param>
500 public void Update(int discardLevel, int packet)
501 { 373 {
502 //Requests for 0 means that the client wants us to resend the whole image
503 //Requests for -1 mean 'update priority but don't change discard level'
504 374
505 if (packet == 0 || packet == -1) 375 // Do we have less then 1 packet's worth of data?
506 return; 376 if (m_asset.Data.Length <= cFirstPacketSize)
507
508 // Check if we've got layers
509 if (Layers.Length > 0)
510 { 377 {
511 DiscardLevel = Util.Clamp<int>(discardLevel, 0, Layers.Length - 1); 378 // Send only 1 packet
512 StopPacket = GetPacketForBytePosition(Layers[(Layers.Length - 1) - DiscardLevel].End); 379 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_asset.Data.Length, m_asset.Data, 2);
513 CurrentPacket = Util.Clamp<int>(packet, 1, TexturePacketCount() - 1); 380 m_stopPacket = 0;
514 // sendFirstPacket = true; 381 return true;
515 } 382 }
516 else 383 else
517 { 384 {
518 // No layers, send full image 385 byte[] firstImageData = new byte[cFirstPacketSize];
519 DiscardLevel = 0; 386 try
520 StopPacket = TexturePacketCount(); 387 {
521 CurrentPacket = Util.Clamp<int>(packet, 1, TexturePacketCount() - 1); 388 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize);
522 } 389 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_asset.Data.Length, firstImageData, 2);
523 }
524
525 /// <summary>
526 /// Sends a texture packet to the client.
527 /// </summary>
528 /// <param name="client">Client to send texture to</param>
529 /// <returns>true if a packet was sent, false if not</returns>
530 public bool SendPacket(LLClientView client)
531 {
532 // If we've hit the end of the send or if the client set -1, return false.
533 if (CurrentPacket > StopPacket || StopPacket == -1)
534 return false;
535
536 // The first packet contains up to 600 bytes and the details of the image. Number of packets, image size in bytes, etc.
537 // This packet only gets sent once unless we're restarting the transfer from 0!
538 if (sendFirstPacket)
539 {
540 sendFirstPacket = false;
541
542 // Do we have less then 1 packet's worth of data?
543 if (m_asset_ref.Data.Length <= FIRST_IMAGE_PACKET_SIZE)
544 {
545 // Send only 1 packet
546 client.SendImageFirstPart(1, requestedUUID , (uint)m_asset_ref.Data.Length, m_asset_ref.Data, 2);
547 CurrentPacket = 2; // Makes it so we don't come back to SendPacket and error trying to send a second packet
548 return true;
549 } 390 }
550 else 391 catch (Exception)
551 { 392 {
552 // Send first packet 393 m_log.Error("Texture block copy failed. Possibly out of memory?");
553 byte[] firstImageData = new byte[FIRST_IMAGE_PACKET_SIZE]; 394 return true;
554 try { Buffer.BlockCopy(m_asset_ref.Data, 0, firstImageData, 0, FIRST_IMAGE_PACKET_SIZE); }
555 catch (Exception)
556 {
557 m_log.Error(String.Format("Err: srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize{3}", m_asset_ref.Data.Length, CurrentBytePosition(), firstImageData.Length, FIRST_IMAGE_PACKET_SIZE));
558
559 //m_log.Error("Texture data copy failed on first packet for " + m_asset_ref.FullID.ToString());
560 //m_cancel = true;
561 //m_sending = false;
562 return false;
563 }
564 client.SendImageFirstPart((ushort)TexturePacketCount(), requestedUUID, (uint)m_asset_ref.Data.Length, firstImageData, 2);
565 ++CurrentPacket; // sets CurrentPacket to 1
566 } 395 }
567 } 396 }
397 return false;
568 398
569 // figure out if we're on the last packet, if so, use the last packet size. If not, use 1000. 399 }
570 // we know that the total image size is greater then 1000 if we're here 400 private bool SendPacket(LLClientView client)
571 int imagePacketSize = (CurrentPacket == (TexturePacketCount() ) ) ? LastPacketSize() : IMAGE_PACKET_SIZE; 401 {
572 402 bool complete = false;
573 //if (imagePacketSize > 0) 403 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize;
574 // imagePacketSize = IMAGE_PACKET_SIZE;
575 //if (imagePacketSize != 1000)
576 // m_log.Debug("ENdPacket");
577 //m_log.Debug(String.Format("srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize{3}", m_asset_ref.Data.Length, CurrentBytePosition(),0, imagePacketSize));
578
579 bool atEnd = false;
580 404
581 // edge case 405 if ((CurrentBytePosition() + cImagePacketSize) > m_asset.Data.Length)
582 if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_asset_ref.Data.Length)
583 { 406 {
584 imagePacketSize = LastPacketSize(); 407 imagePacketSize = LastPacketSize();
585 atEnd = true; 408 complete=true;
586 // edge case 2! 409 if ((CurrentBytePosition() + imagePacketSize) > m_asset.Data.Length)
587 if ((CurrentBytePosition() + imagePacketSize) > m_asset_ref.Data.Length)
588 { 410 {
589 imagePacketSize = m_asset_ref.Data.Length - CurrentBytePosition(); 411 imagePacketSize = m_asset.Data.Length - CurrentBytePosition();
590 atEnd = true; 412 complete = true;
591 } 413 }
592 } 414 }
415
416 //It's concievable that the client might request packet one
417 //from a one packet image, which is really packet 0,
418 //which would leave us with a negative imagePacketSize..
419 if (imagePacketSize > 0)
420 {
421 byte[] imageData = new byte[imagePacketSize];
422 try
423 {
424 Buffer.BlockCopy(m_asset.Data, CurrentBytePosition(), imageData, 0, imagePacketSize);
425 }
426 catch (Exception e)
427 {
428 m_log.Error("Error copying texture block. Out of memory? imagePacketSize was " + imagePacketSize.ToString() + " on packet " + m_packetNumber.ToString() + " out of " + m_stopPacket.ToString() + ". Exception: " + e.ToString());
429 return false;
430 }
593 431
594 byte[] imageData = new byte[imagePacketSize]; 432 //Send the packet
595 try { Buffer.BlockCopy(m_asset_ref.Data, CurrentBytePosition(), imageData, 0, imagePacketSize); } 433 client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData);
596 catch (Exception e) 434
435 }
436 if (complete)
597 { 437 {
598 m_log.Error(String.Format("Err: srcLen:{0}, BytePos:{1}, desLen:{2}, pktsize:{3}, currpak:{4}, stoppak:{5}, totalpak:{6}", m_asset_ref.Data.Length, CurrentBytePosition(),
599 imageData.Length, imagePacketSize, CurrentPacket, StopPacket, TexturePacketCount()));
600 m_log.Error(e.ToString());
601 //m_log.Error("Texture data copy failed for " + m_asset_ref.FullID.ToString());
602 //m_cancel = true;
603 //m_sending = false;
604 return false; 438 return false;
605 } 439 }
440 else
441 {
442 return true;
443 }
606 444
607 // Send next packet to the client
608 client.SendImageNextPart((ushort)(CurrentPacket - 1), requestedUUID, imageData);
609 445
610 ++CurrentPacket; 446 }
447 public bool SendPackets(LLClientView client)
448 {
611 449
612 if (atEnd) 450 if (!m_completedSendAtCurrentDiscardLevel)
613 CurrentPacket = StopPacket + 1; 451 {
452 if (m_packetNumber <= m_stopPacket)
453 {
614 454
615 return true; 455 bool SendMore = true;
616 } 456 if (!m_sentinfo || (m_packetNumber == 0))
457 {
458 if (SendFirstPacket(client))
459 {
460 SendMore = false;
461 }
462 m_sentinfo = true;
463 m_packetNumber++;
464 }
617 465
618 } 466 if (m_packetNumber < 2)
467 {
468 m_packetNumber = 2;
469 }
470
471 int count=0;
472 while (SendMore && count < 5 && m_packetNumber <= m_stopPacket)
473 {
474 count++;
475 SendMore = SendPacket(client);
476 m_packetNumber++;
477 }
478 if (m_packetNumber > m_stopPacket)
479 {
619 480
620 /// <summary> 481 return true;
621 /// Generic Priority Queue element
622 /// Contains a Priority and a Reference type Data Element
623 /// </summary>
624 /// <typeparam name="D">Reference type data element</typeparam>
625 struct Prio<D> : IComparable<Prio<D>> where D : class
626 {
627 public D data;
628 private int priority;
629 482
630 public Prio(D data, int priority) 483 }
631 {
632 this.data = data;
633 this.priority = priority;
634 }
635 484
636 public int CompareTo(Prio<D> that) 485 }
637 {
638 return priority.CompareTo(that.priority);
639 }
640 486
641 public bool Equals(Prio<D> that) 487 }
642 { 488 return false;
643 return priority == that.priority;
644 } 489 }
645 490
646 public static Prio<D> operator +(Prio<D> tp, int delta) 491 public void RunUpdate()
647 { 492 {
648 return new Prio<D>(tp.data, tp.priority + delta); 493 //This is where we decide what we need to update
649 } 494 //and assign the real discardLevel and packetNumber
495 //assuming of course that the connected client might be bonkers
650 496
651 public static bool operator <(Prio<D> tp, int check) 497 if (!m_hasasset)
652 { 498 {
653 return (tp.priority < check);
654 }
655 499
656 public static bool operator >(Prio<D> tp, int check) 500 if (!m_asset_requested)
657 { 501 {
658 return (tp.priority > check); 502 m_asset_requested = true;
659 } 503 m_assetCache.GetAsset(m_requestedUUID, AssetDataCallback, true);
660 504
661 public static Prio<D> operator -(Prio<D> tp, int delta) 505 }
662 { 506
663 if (tp.priority - delta < 0) 507 }
664 return new Prio<D>(tp.data, tp.priority - delta);
665 else 508 else
666 return new Prio<D>(tp.data, 0); 509 {
667 }
668 510
669 public override String ToString()
670 {
671 return String.Format("{0}[{1}]", data, priority);
672 }
673 511
674 internal Prio<D> SetPriority(int pPriority) 512 if (!m_decoded)
675 { 513 {
676 return new Prio<D>(this.data, pPriority); 514 //We need to decode the requested image first
515 if (!m_decoderequested)
516 {
517 //Request decode
518 m_decoderequested = true;
519 // Do we have a jpeg decoder?
520 if (m_j2kDecodeModule != null)
521 {
522 // Send it off to the jpeg decoder
523 m_j2kDecodeModule.decode(m_requestedUUID, Data, J2KDecodedCallback);
524
525 }
526 else
527 {
528 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
529 }
530 }
531
532 }
533 else
534 {
535
536
537 //discardLevel of -1 means just update the priority
538 if (m_requestedDiscardLevel != -1)
539 {
540
541 //Evaluate the discard level
542 //First, is it positive?
543 if (m_requestedDiscardLevel >= 0)
544 {
545 if (m_requestedDiscardLevel > Layers.Length - 1)
546 {
547 m_discardLevel = (sbyte)(Layers.Length - 1);
548 }
549 else
550 {
551 m_discardLevel = m_requestedDiscardLevel;
552 }
553
554 //Calculate the m_stopPacket
555 if (Layers.Length > 0)
556 {
557 m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - m_discardLevel].End);
558 }
559 else
560 {
561 m_stopPacket = TexturePacketCount();
562 }
563 //Don't reset packet number unless we're waiting or it's ahead of us
564 if (m_completedSendAtCurrentDiscardLevel || m_requestedPacketNumber>m_packetNumber)
565 {
566 m_packetNumber = m_requestedPacketNumber;
567 }
568
569 if (m_packetNumber <= m_stopPacket)
570 {
571 m_completedSendAtCurrentDiscardLevel = false;
572 }
573
574 }
575
576 }
577 }
578 }
677 } 579 }
580
678 } 581 }
679} 582}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
index 6bfa864..ef1f34a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
@@ -82,6 +82,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
82 internal LLPacketThrottle AssetThrottle; 82 internal LLPacketThrottle AssetThrottle;
83 internal LLPacketThrottle TextureThrottle; 83 internal LLPacketThrottle TextureThrottle;
84 internal LLPacketThrottle TotalThrottle; 84 internal LLPacketThrottle TotalThrottle;
85
86 /// <summary>
87 /// The number of packets in the OutgoingPacketQueue
88 ///
89 /// </summary>
90 internal int TextureOutgoingPacketQueueCount
91 {
92 get
93 {
94 if (TextureOutgoingPacketQueue == null)
95 return 0;
96 return TextureOutgoingPacketQueue.Count;
97 }
98 }
85 99
86 // private long LastThrottle; 100 // private long LastThrottle;
87 // private long ThrottleInterval; 101 // private long ThrottleInterval;
diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs
index 3b83cc2..cdbf1db 100644
--- a/OpenSim/Region/UserStatistics/WebStatsModule.cs
+++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs
@@ -676,7 +676,20 @@ namespace OpenSim.Region.UserStatistics
676 { 676 {
677 m_log.Debug("INSERT"); 677 m_log.Debug("INSERT");
678 updatecmd.CommandText = SQL_STATS_TABLE_INSERT; 678 updatecmd.CommandText = SQL_STATS_TABLE_INSERT;
679 updatecmd.ExecuteNonQuery(); 679 try
680 {
681 updatecmd.ExecuteNonQuery();
682 }
683 catch
684 (SqliteExecutionException)
685 {
686 m_log.Warn("[WEBSTATS]: failed to write stats to storage Execution Exception");
687 }
688 catch (SqliteSyntaxException)
689 {
690 m_log.Warn("[WEBSTATS]: failed to write stats to storage SQL Syntax Exception");
691 }
692
680 } 693 }
681 } 694 }
682 695