aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
diff options
context:
space:
mode:
authorJohn Hurliman2009-10-01 17:42:13 -0700
committerJohn Hurliman2009-10-01 17:42:13 -0700
commit6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2 (patch)
tree0de3c250460102fc59e9373e4bb33de2afc5ef3a /OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
parentFixing LLClientView memory leak (diff)
downloadopensim-SC_OLD-6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2.zip
opensim-SC_OLD-6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2.tar.gz
opensim-SC_OLD-6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2.tar.bz2
opensim-SC_OLD-6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2.tar.xz
* Rewrote LLImageManager to use a real priority queue and hold minimal state
* Rewrote the logic in J2KImage.RunUpdate() * Added a default avatar texture (I made it myself)
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs455
1 files changed, 214 insertions, 241 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index d86b123..1448722 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -38,44 +38,37 @@ using System.Reflection;
38namespace OpenSim.Region.ClientStack.LindenUDP 38namespace OpenSim.Region.ClientStack.LindenUDP
39{ 39{
40 /// <summary> 40 /// <summary>
41 /// We use this class to store image data and associated request data and attributes 41 /// Stores information about a current texture download and a reference to the texture asset
42 /// </summary> 42 /// </summary>
43 public class J2KImage 43 public class J2KImage
44 { 44 {
45 private const int IMAGE_PACKET_SIZE = 1000;
46 private const int FIRST_PACKET_SIZE = 600;
47
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 49
47 public double m_designatedPriorityKey; 50 public uint m_lastSequence;
48 public double m_requestedPriority = 0.0d; 51 public float m_requestedPriority;
49 public uint m_lastSequence = 0;
50 public uint m_requestedPacketNumber; 52 public uint m_requestedPacketNumber;
51 public sbyte m_requestedDiscardLevel; 53 public sbyte m_requestedDiscardLevel;
52 public UUID m_requestedUUID; 54 public UUID m_requestedUUID;
53 public IJ2KDecoder m_j2kDecodeModule; 55 public IJ2KDecoder m_j2kDecodeModule;
54 public IAssetService m_assetCache; 56 public IAssetService m_assetCache;
55 public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0]; 57 public OpenJPEG.J2KLayerInfo[] m_layers;
56 public AssetBase m_MissingSubstitute = null; 58 public bool m_decoded;
57 public bool m_decoded = false; 59 public bool m_hasasset;
58 public bool m_completedSendAtCurrentDiscardLevel; 60 public C5.IPriorityQueueHandle<J2KImage> m_priorityQueueHandle;
59 61
60 private sbyte m_discardLevel=-1;
61 private uint m_packetNumber; 62 private uint m_packetNumber;
62 private bool m_decoderequested = false; 63 private bool m_decoderequested;
63 public bool m_hasasset = false; 64 private bool m_asset_requested;
64 private bool m_asset_requested = false; 65 private bool m_sentinfo;
65 private bool m_sentinfo = false; 66 private uint m_stopPacket;
66 private uint m_stopPacket = 0; 67 private AssetBase m_asset;
67 private const int cImagePacketSize = 1000; 68 private int m_assetDataLength;
68 private const int cFirstPacketSize = 600; 69 private LLImageManager m_imageManager;
69
70 private AssetBase m_asset = null;
71 private int m_assetDataLength = 0;
72
73 private LLImageManager m_image;
74 70
75 public J2KImage(LLImageManager image) 71 #region Properties
76 {
77 m_image = image;
78 }
79 72
80 public uint m_pPacketNumber 73 public uint m_pPacketNumber
81 { 74 {
@@ -88,10 +81,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
88 81
89 public byte[] Data 82 public byte[] Data
90 { 83 {
91 get 84 get
92 { 85 {
93 if (m_asset != null) 86 if (m_asset != null)
94 return m_asset.Data; 87 return m_asset.Data;
95 else 88 else
96 return null; 89 return null;
97 } 90 }
@@ -101,9 +94,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
101 { 94 {
102 if (!m_decoded) 95 if (!m_decoded)
103 return 0; 96 return 0;
97
104 try 98 try
105 { 99 {
106 return (ushort)(((m_assetDataLength - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); 100 return (ushort)(((m_assetDataLength - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1);
107 } 101 }
108 catch (Exception) 102 catch (Exception)
109 { 103 {
@@ -114,127 +108,145 @@ namespace OpenSim.Region.ClientStack.LindenUDP
114 } 108 }
115 } 109 }
116 110
117 public void DropAsset() 111 #endregion Properties
118 {
119 //m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID);
120 m_asset = null;
121 m_hasasset = false;
122 m_asset_requested = false;
123 }
124 112
125 public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) 113 public J2KImage(LLImageManager imageManager)
126 { 114 {
127 m_image.m_outstandingtextures++; 115 m_imageManager = imageManager;
128 Layers = layers;
129 m_decoded = true;
130 RunUpdate();
131 } 116 }
132 117
133 public void AssetDataCallback(UUID AssetID, AssetBase asset) 118 public bool SendPackets(LLClientView client, int maxpack)
134 { 119 {
135 m_hasasset = true; 120 if (m_packetNumber <= m_stopPacket)
136 if (asset == null || asset.Data == null)
137 {
138 m_asset = m_MissingSubstitute;
139 }
140 else
141 { 121 {
142 m_asset = asset; 122 bool SendMore = true;
143 } 123 if (!m_sentinfo || (m_packetNumber == 0))
144 124 {
145 m_assetDataLength = m_asset.Data.Length; 125 if (SendFirstPacket(client))
146 126 {
147 RunUpdate(); 127 SendMore = false;
148 } 128 }
149 129 m_sentinfo = true;
150 protected void AssetReceived(string id, Object sender, AssetBase asset) 130 m_packetNumber++;
151 { 131 }
152 UUID assetID = UUID.Zero; 132 // bool ignoreStop = false;
153 if (asset != null) 133 if (m_packetNumber < 2)
154 assetID = asset.FullID; 134 {
155 135 m_packetNumber = 2;
156 AssetDataCallback(assetID, asset); 136 }
157
158 }
159 137
160 private int GetPacketForBytePosition(int bytePosition) 138 int count = 0;
161 { 139 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket)
162 return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1; 140 {
163 } 141 count++;
142 SendMore = SendPacket(client);
143 m_packetNumber++;
144 }
164 145
165 public int LastPacketSize() 146 if (m_packetNumber > m_stopPacket)
166 { 147 return true;
167 if (m_packetNumber == 1)
168 return m_assetDataLength;
169 int lastsize = (m_assetDataLength - cFirstPacketSize) % cImagePacketSize;
170 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
171 if (lastsize == 0)
172 {
173 lastsize = cImagePacketSize;
174 } 148 }
175 return lastsize;
176 }
177
178 public int CurrentBytePosition()
179 {
180 if (m_packetNumber == 0)
181 return 0;
182 if (m_packetNumber == 1)
183 return cFirstPacketSize;
184 149
185 int result = cFirstPacketSize + ((int)m_packetNumber - 2) * cImagePacketSize; 150 return false;
186 if (result < 0)
187 {
188 result = cFirstPacketSize;
189 }
190 return result;
191 } 151 }
192 152
193 public bool SendFirstPacket(LLClientView client) 153 public void RunUpdate()
194 { 154 {
195 // this means we don't have 155 //This is where we decide what we need to update
196 if (Data == null) 156 //and assign the real discardLevel and packetNumber
197 { 157 //assuming of course that the connected client might be bonkers
198 client.SendImageNotFound(m_requestedUUID); 158
199 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); 159 if (!m_hasasset)
200 return true;
201 }
202 // Do we have less then 1 packet's worth of data?
203 else if (m_assetDataLength <= cFirstPacketSize)
204 { 160 {
205 // Send only 1 packet 161 if (!m_asset_requested)
206 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2); 162 {
207 m_stopPacket = 0; 163 m_asset_requested = true;
208 return true; 164 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived);
165 }
209 } 166 }
210 else 167 else
211 { 168 {
212 byte[] firstImageData = new byte[cFirstPacketSize]; 169 if (!m_decoded)
213 try 170 {
214 { 171 //We need to decode the requested image first
215 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); 172 if (!m_decoderequested)
216 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2); 173 {
174 //Request decode
175 m_decoderequested = true;
176 // Do we have a jpeg decoder?
177 if (m_j2kDecodeModule != null)
178 {
179 if (Data == null)
180 {
181 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
182 }
183 else
184 {
185 // Send it off to the jpeg decoder
186 m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback);
187 }
188
189 }
190 else
191 {
192 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
193 }
194 }
217 } 195 }
218 catch (Exception) 196 else
219 { 197 {
220 m_log.Error("Texture block copy failed. Possibly out of memory?"); 198 // Check for missing image asset data
221 return true; 199 if (m_asset == null || m_asset.Data == null)
200 {
201 // FIXME:
202 m_packetNumber = m_stopPacket;
203 return;
204 }
205
206 if (m_requestedDiscardLevel >= 0 || m_stopPacket == 0)
207 {
208 int maxDiscardLevel = Math.Max(0, m_layers.Length - 1);
209
210 // Treat initial texture downloads with a DiscardLevel of -1 a request for the highest DiscardLevel
211 if (m_requestedDiscardLevel < 0 && m_stopPacket == 0)
212 m_requestedDiscardLevel = (sbyte)maxDiscardLevel;
213
214 // Clamp at the highest discard level
215 m_requestedDiscardLevel = (sbyte)Math.Min(m_requestedDiscardLevel, maxDiscardLevel);
216
217 //Calculate the m_stopPacket
218 if (m_layers.Length > 0)
219 {
220 m_stopPacket = (uint)GetPacketForBytePosition(m_layers[(m_layers.Length - 1) - m_requestedDiscardLevel].End);
221 //I don't know why, but the viewer seems to expect the final packet if the file
222 //is just one packet bigger.
223 if (TexturePacketCount() == m_stopPacket + 1)
224 {
225 m_stopPacket = TexturePacketCount();
226 }
227 }
228 else
229 {
230 m_stopPacket = TexturePacketCount();
231 }
232
233 m_packetNumber = m_requestedPacketNumber;
234 }
222 } 235 }
223 } 236 }
224 return false;
225 } 237 }
226 238
227 private bool SendPacket(LLClientView client) 239 private bool SendPacket(LLClientView client)
228 { 240 {
229 bool complete = false; 241 bool complete = false;
230 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize; 242 int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE;
231 243
232 try 244 try
233 { 245 {
234 if ((CurrentBytePosition() + cImagePacketSize) > m_assetDataLength) 246 if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_assetDataLength)
235 { 247 {
236 imagePacketSize = LastPacketSize(); 248 imagePacketSize = LastPacketSize();
237 complete=true; 249 complete = true;
238 if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength) 250 if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength)
239 { 251 {
240 imagePacketSize = m_assetDataLength - CurrentBytePosition(); 252 imagePacketSize = m_assetDataLength - CurrentBytePosition();
@@ -259,7 +271,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
259 } 271 }
260 272
261 //Send the packet 273 //Send the packet
262 client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData); 274 client.SendImageNextPart((ushort)(m_packetNumber - 1), m_requestedUUID, imageData);
263 } 275 }
264 if (complete) 276 if (complete)
265 { 277 {
@@ -275,146 +287,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP
275 return false; 287 return false;
276 } 288 }
277 } 289 }
278 public bool SendPackets(LLClientView client, int maxpack) 290
291 private int GetPacketForBytePosition(int bytePosition)
279 { 292 {
293 return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
294 }
280 295
281 if (!m_completedSendAtCurrentDiscardLevel) 296 private int LastPacketSize()
297 {
298 if (m_packetNumber == 1)
299 return m_assetDataLength;
300 int lastsize = (m_assetDataLength - FIRST_PACKET_SIZE) % IMAGE_PACKET_SIZE;
301 //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary
302 if (lastsize == 0)
282 { 303 {
283 if (m_packetNumber <= m_stopPacket) 304 lastsize = IMAGE_PACKET_SIZE;
284 {
285 bool SendMore = true;
286 if (!m_sentinfo || (m_packetNumber == 0))
287 {
288 if (SendFirstPacket(client))
289 {
290 SendMore = false;
291 }
292 m_sentinfo = true;
293 m_packetNumber++;
294 }
295 // bool ignoreStop = false;
296 if (m_packetNumber < 2)
297 {
298 m_packetNumber = 2;
299 }
300
301 int count = 0;
302 while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket)
303 {
304 count++;
305 SendMore = SendPacket(client);
306 m_packetNumber++;
307 }
308
309 if (m_packetNumber > m_stopPacket)
310 {
311 return true;
312 }
313 }
314 } 305 }
315 return false; 306 return lastsize;
316 } 307 }
317 308
318 public void RunUpdate() 309 private int CurrentBytePosition()
319 { 310 {
320 //This is where we decide what we need to update 311 if (m_packetNumber == 0)
321 //and assign the real discardLevel and packetNumber 312 return 0;
322 //assuming of course that the connected client might be bonkers 313 if (m_packetNumber == 1)
314 return FIRST_PACKET_SIZE;
323 315
324 if (!m_hasasset) 316 int result = FIRST_PACKET_SIZE + ((int)m_packetNumber - 2) * IMAGE_PACKET_SIZE;
317 if (result < 0)
325 { 318 {
319 result = FIRST_PACKET_SIZE;
320 }
321 return result;
322 }
326 323
327 if (!m_asset_requested) 324 private bool SendFirstPacket(LLClientView client)
325 {
326 // this means we don't have
327 if (Data == null)
328 {
329 client.SendImageNotFound(m_requestedUUID);
330 m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID);
331 return true;
332 }
333 // Do we have less then 1 packet's worth of data?
334 else if (m_assetDataLength <= FIRST_PACKET_SIZE)
335 {
336 // Send only 1 packet
337 client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2);
338 m_stopPacket = 0;
339 return true;
340 }
341 else
342 {
343 byte[] firstImageData = new byte[FIRST_PACKET_SIZE];
344 try
328 { 345 {
329 m_asset_requested = true; 346 Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)FIRST_PACKET_SIZE);
330 m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); 347 client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2);
331 348 }
349 catch (Exception)
350 {
351 m_log.Error("Texture block copy failed. Possibly out of memory?");
352 return true;
332 } 353 }
354 }
355 return false;
356 }
357
358 private void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers)
359 {
360 m_layers = layers;
361 m_decoded = true;
362 RunUpdate();
363 }
364
365 private void AssetDataCallback(UUID AssetID, AssetBase asset)
366 {
367 m_hasasset = true;
333 368
369 if (asset == null || asset.Data == null)
370 {
371 m_asset = null;
372 m_decoded = true;
334 } 373 }
335 else 374 else
336 { 375 {
376 m_asset = asset;
377 m_assetDataLength = m_asset.Data.Length;
378 }
337 379
380 RunUpdate();
381 }
338 382
339 if (!m_decoded) 383 private void AssetReceived(string id, Object sender, AssetBase asset)
340 { 384 {
341 //We need to decode the requested image first 385 UUID assetID = UUID.Zero;
342 if (!m_decoderequested) 386 if (asset != null)
343 { 387 assetID = asset.FullID;
344 //Request decode
345 m_decoderequested = true;
346 // Do we have a jpeg decoder?
347 if (m_j2kDecodeModule != null)
348 {
349 if (Data == null)
350 {
351 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
352 }
353 else
354 {
355 // Send it off to the jpeg decoder
356 m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback);
357 }
358
359 }
360 else
361 {
362 J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]);
363 }
364 }
365
366 }
367 else
368 {
369 //discardLevel of -1 means just update the priority
370 if (m_requestedDiscardLevel != -1)
371 {
372 //Evaluate the discard level
373 //First, is it positive?
374 if (m_requestedDiscardLevel >= 0)
375 {
376 if (m_requestedDiscardLevel > Layers.Length - 1)
377 {
378 m_discardLevel = (sbyte)(Layers.Length - 1);
379 }
380 else
381 {
382 m_discardLevel = m_requestedDiscardLevel;
383 }
384 388
385 //Calculate the m_stopPacket 389 AssetDataCallback(assetID, asset);
386 if (Layers.Length > 0)
387 {
388 m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - m_discardLevel].End);
389 //I don't know why, but the viewer seems to expect the final packet if the file
390 //is just one packet bigger.
391 if (TexturePacketCount() == m_stopPacket + 1)
392 {
393 m_stopPacket = TexturePacketCount();
394 }
395 }
396 else
397 {
398 m_stopPacket = TexturePacketCount();
399 }
400 //Don't reset packet number unless we're waiting or it's ahead of us
401 if (m_completedSendAtCurrentDiscardLevel || m_requestedPacketNumber>m_packetNumber)
402 {
403 m_packetNumber = m_requestedPacketNumber;
404 }
405 390
406 if (m_packetNumber <= m_stopPacket)
407 {
408 m_completedSendAtCurrentDiscardLevel = false;
409 }
410 }
411 }
412 else
413 {
414 m_packetNumber = m_stopPacket;
415 }
416 }
417 }
418 } 391 }
419 } 392 }
420} 393}