From 4bf47fa592fd60716ffd6b34d418c241709c4744 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 30 Sep 2009 10:59:10 -0700 Subject: This releases the texture assets from LLImageManager cache, and re-requests them later if the client asks for them again. Needs more testing in texture-rich sims. --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 37 +++++++++++++++------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 23 +++++++++++++- 2 files changed, 48 insertions(+), 12 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 6cffd70..b186720 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -60,14 +60,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP private sbyte m_discardLevel=-1; private uint m_packetNumber; private bool m_decoderequested = false; - private bool m_hasasset = false; + public bool m_hasasset = false; private bool m_asset_requested = false; private bool m_sentinfo = false; private uint m_stopPacket = 0; private const int cImagePacketSize = 1000; private const int cFirstPacketSize = 600; + private AssetBase m_asset = null; + private int m_assetDataLength = 0; + private LLImageManager m_image; + public J2KImage(LLImageManager image) { m_image = image; @@ -99,7 +103,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return 0; try { - return (ushort)(((m_asset.Data.Length - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); + return (ushort)(((m_assetDataLength - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); } catch (Exception) { @@ -110,6 +114,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } + public void DropAsset() + { + m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID); + m_asset = null; + m_hasasset = false; + m_asset_requested = false; + } + public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) { m_image.m_outstandingtextures++; @@ -127,8 +139,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - m_asset = asset; + m_asset = asset; } + + m_assetDataLength = m_asset.Data.Length; + RunUpdate(); } @@ -150,8 +165,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int LastPacketSize() { if (m_packetNumber == 1) - return m_asset.Data.Length; - int lastsize = (m_asset.Data.Length - cFirstPacketSize) % cImagePacketSize; + return m_assetDataLength; + int lastsize = (m_assetDataLength - cFirstPacketSize) % cImagePacketSize; //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary if (lastsize == 0) { @@ -185,10 +200,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } // Do we have less then 1 packet's worth of data? - else if (m_asset.Data.Length <= cFirstPacketSize) + else if (m_assetDataLength <= cFirstPacketSize) { // Send only 1 packet - client.SendImageFirstPart(1, m_requestedUUID, (uint)m_asset.Data.Length, m_asset.Data, 2); + client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2); m_stopPacket = 0; return true; } @@ -198,7 +213,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP try { Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); - client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_asset.Data.Length, firstImageData, 2); + client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2); } catch (Exception) { @@ -216,13 +231,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP try { - if ((CurrentBytePosition() + cImagePacketSize) > m_asset.Data.Length) + if ((CurrentBytePosition() + cImagePacketSize) > m_assetDataLength) { imagePacketSize = LastPacketSize(); complete=true; - if ((CurrentBytePosition() + imagePacketSize) > m_asset.Data.Length) + if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength) { - imagePacketSize = m_asset.Data.Length - CurrentBytePosition(); + imagePacketSize = m_assetDataLength - CurrentBytePosition(); complete = true; } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 295a5e6..b039049 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -257,6 +257,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KImage imagereq = m_imagestore[m_priorities.Values[x]]; if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel) { + // we need to test this here now that we are dropping assets + if (!imagereq.m_hasasset) + { + m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID); + imagereq.RunUpdate(); + continue; + } + numCollected++; //SendPackets will send up to ten packets per cycle if (imagereq.SendPackets(m_client, maxpack)) @@ -264,8 +272,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP //Send complete if (!imagereq.m_completedSendAtCurrentDiscardLevel) { - imagereq.m_completedSendAtCurrentDiscardLevel = true; + // I think this field imagereq.m_completedSendAtCurrentDiscardLevel + // is completely redundant + //imagereq.m_completedSendAtCurrentDiscardLevel = true; + Interlocked.Decrement(ref m_outstandingtextures); + + // First and foremost, drop the reference to the asset + // so that the asset doesn't stay in memory forever. + // We'll Get it again from the asset service (usually cache) + // if/when the client requests it again. + // In order not to mess much with the current implementation + // of this management code, we drop only the asset reference + // but keep the image request itself. + imagereq.DropAsset(); + //Re-assign priority to bottom //Remove the old priority m_priorities.Remove(imagereq.m_designatedPriorityKey); -- cgit v1.1 From 22cc31135e2989df28e0756eb3b03f85530d5555 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 29 Sep 2009 18:15:22 -0700 Subject: Attempting to improve the robustness of texture decoding by always ignoring LayerInfo.End values and creating guessed default layer boundaries on failed decodes Changed a noisy J2K decode log message from Info to Debug Replacing openjpeg-dotnet decoding with managed CSJ2K decoding. Should be much more reliable, faster, and use less memory * Re-added openjpeg-dotnet files since they are used elsewhere in OpenSim * Updated prebuild.xml with a reference to CSJ2K --- .../Agent/TextureSender/J2KDecoderModule.cs | 236 ++++++--------------- 1 file changed, 68 insertions(+), 168 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs index 937f76b..aa29947 100644 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs @@ -34,8 +34,8 @@ using System.Threading; using log4net; using Nini.Config; using OpenMetaverse; -using OpenMetaverse.Assets; using OpenMetaverse.Imaging; +using CSJ2K; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -195,174 +195,58 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender { int DecodeTime = 0; DecodeTime = Environment.TickCount; - OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality + OpenJPEG.J2KLayerInfo[] layers = null; - if (!OpenJpegFail) + if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) { - if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) + try { - try + List layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kdata)); + + if (layerStarts != null && layerStarts.Count > 0) { + layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count]; - AssetTexture texture = new AssetTexture(AssetId, j2kdata); - if (texture.DecodeLayerBoundaries()) + for (int i = 0; i < layerStarts.Count; i++) { - bool sane = true; - - // Sanity check all of the layers - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - if (texture.LayerInfo[i].End > texture.AssetData.Length) - { - sane = false; - break; - } - } - - if (sane) - { - layers = texture.LayerInfo; - fCache.SaveFileCacheForAsset(AssetId, layers); - - - // Write out decode time - m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", Environment.TickCount - DecodeTime, - AssetId); - - } + OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); + int start = layerStarts[i]; + + if (i == 0) + layer.Start = 0; else - { - m_log.WarnFormat( - "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}", - AssetId); - } - } + layer.Start = layerStarts[i]; - else - { - /* - Random rnd = new Random(); - // scramble ends for test - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - texture.LayerInfo[i].End = rnd.Next(999999); - } - */ - - // Try to do some heuristics error correction! Yeah. - bool sane2Heuristics = true; - - - if (texture.Image == null) - sane2Heuristics = false; - - if (texture.LayerInfo == null) - sane2Heuristics = false; - - if (sane2Heuristics) - { - - - if (texture.LayerInfo.Length == 0) - sane2Heuristics = false; - } - - if (sane2Heuristics) - { - // Last layer start is less then the end of the file and last layer start is greater then 0 - if (texture.LayerInfo[texture.LayerInfo.Length - 1].Start < texture.AssetData.Length && texture.LayerInfo[texture.LayerInfo.Length - 1].Start > 0) - { - } - else - { - sane2Heuristics = false; - } - - } - - if (sane2Heuristics) - { - int start = 0; - - // try to fix it by using consistant data in the start field - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - if (i == 0) - start = 0; - - if (i == texture.LayerInfo.Length - 1) - texture.LayerInfo[i].End = texture.AssetData.Length; - else - texture.LayerInfo[i].End = texture.LayerInfo[i + 1].Start - 1; - - // in this case, the end of the next packet is less then the start of the last packet - // after we've attempted to fix it which means the start of the last packet is borked - // there's no recovery from this - if (texture.LayerInfo[i].End < start) - { - sane2Heuristics = false; - break; - } - - if (texture.LayerInfo[i].End < 0 || texture.LayerInfo[i].End > texture.AssetData.Length) - { - sane2Heuristics = false; - break; - } - - if (texture.LayerInfo[i].Start < 0 || texture.LayerInfo[i].Start > texture.AssetData.Length) - { - sane2Heuristics = false; - break; - } - - start = texture.LayerInfo[i].Start; - } - } - - if (sane2Heuristics) - { - layers = texture.LayerInfo; - fCache.SaveFileCacheForAsset(AssetId, layers); - - - // Write out decode time - m_log.InfoFormat("[J2KDecoderModule]: HEURISTICS SUCCEEDED {0} Decode Time: {1}", Environment.TickCount - DecodeTime, - AssetId); - - } + if (i == layerStarts.Count - 1) + layer.End = j2kdata.Length; else - { - m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}. Is this a texture? is it J2K?", AssetId); - } + layer.End = layerStarts[i + 1] - 1; + + layers[i] = layer; } - texture = null; // dereference and dispose of ManagedImage - } - catch (DllNotFoundException) - { - m_log.Error( - "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!"); - OpenJpegFail = true; - } - catch (Exception ex) - { - m_log.WarnFormat( - "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}", - AssetId, ex); } } - - } - - // Cache Decoded layers - lock (m_cacheddecode) - { - if (m_cacheddecode.ContainsKey(AssetId)) - m_cacheddecode.Remove(AssetId); - m_cacheddecode.Add(AssetId, layers); + catch (Exception ex) + { + m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + AssetId + ": " + ex.ToString()); + } - } + if (layers.Length == 0) + { + m_log.Warn("[J2KDecoderModule]: OpenJPEG failed to decode any layer data for texture " + AssetId + ", guessing sane defaults"); + // Layer decoding completely failed. Guess at sane defaults for the layer boundaries + layers = CreateDefaultLayers(j2kdata.Length); + } + // Cache Decoded layers + lock (m_cacheddecode) + { + if (m_cacheddecode.ContainsKey(AssetId)) + m_cacheddecode.Remove(AssetId); + m_cacheddecode.Add(AssetId, layers); + } + } + // Notify Interested Parties lock (m_notifyList) { @@ -377,6 +261,31 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender } } } + + private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength) + { + OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5]; + layers[0] = new OpenJPEG.J2KLayerInfo(); + + for (int i = 0; i < layers.Length; i++) + { + OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); + + if (i == 0) + layer.Start = 0; + else + layer.Start = layers[i - 1].End + 1; + + // These default layer sizes are based on a small sampling of real-world texture data + // with extra padding thrown in for good measure. This is a worst case fallback plan + // and will probably not gracefully handle all real world data + layer.End = (int)(160d * Math.Exp(1.3d * (double)(i + 1))); + + layers[i] = layer; + } + + return layers; + } private void CleanCache() { @@ -418,10 +327,9 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender { m_cacheDecodeFolder = pFolder; m_cacheTimeout = timeout; + if (!Directory.Exists(pFolder)) - { Createj2KCacheFolder(pFolder); - } } /// @@ -447,14 +355,15 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); } + fsSWCache.Write(stringResult.ToString()); fsSWCache.Close(); fsSWCache.Dispose(); fsCache.Dispose(); + return true; } - return false; } @@ -475,11 +384,9 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender return false; if (!enabled) - { return false; - } - string readResult = string.Empty; + string readResult = String.Empty; try { @@ -493,7 +400,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender sr.Close(); sr.Dispose(); fsCachefile.Dispose(); - } catch (IOException ioe) { @@ -514,7 +420,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender "[J2KDecodeCache]: Cache Read failed. IO Exception."); } return false; - } catch (UnauthorizedAccessException) { @@ -541,7 +446,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender m_log.Error( "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!"); enabled = false; - return false; } catch (Exception e) @@ -581,7 +485,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender Layers[i] = new OpenJPEG.J2KLayerInfo(); Layers[i].Start = element1; Layers[i].End = element2; - } else { @@ -592,9 +495,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender } } - - - return true; } -- cgit v1.1 From f908e32f620272a63c6a338fddfa3214ce95627b Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 30 Sep 2009 19:26:26 +0100 Subject: Revert "Attempting to improve the robustness of texture decoding by always ignoring LayerInfo.End values and creating guessed default layer boundaries on failed decodes Changed a noisy J2K decode log message from Info to Debug" This reverts commit 22cc31135e2989df28e0756eb3b03f85530d5555. --- .../Agent/TextureSender/J2KDecoderModule.cs | 236 +++++++++++++++------ 1 file changed, 168 insertions(+), 68 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs index aa29947..937f76b 100644 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs @@ -34,8 +34,8 @@ using System.Threading; using log4net; using Nini.Config; using OpenMetaverse; +using OpenMetaverse.Assets; using OpenMetaverse.Imaging; -using CSJ2K; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -195,58 +195,174 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender { int DecodeTime = 0; DecodeTime = Environment.TickCount; - OpenJPEG.J2KLayerInfo[] layers = null; + OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality - if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) + if (!OpenJpegFail) { - try + if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) { - List layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kdata)); - - if (layerStarts != null && layerStarts.Count > 0) + try { - layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count]; - for (int i = 0; i < layerStarts.Count; i++) + AssetTexture texture = new AssetTexture(AssetId, j2kdata); + if (texture.DecodeLayerBoundaries()) { - OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); - int start = layerStarts[i]; - - if (i == 0) - layer.Start = 0; + bool sane = true; + + // Sanity check all of the layers + for (int i = 0; i < texture.LayerInfo.Length; i++) + { + if (texture.LayerInfo[i].End > texture.AssetData.Length) + { + sane = false; + break; + } + } + + if (sane) + { + layers = texture.LayerInfo; + fCache.SaveFileCacheForAsset(AssetId, layers); + + + // Write out decode time + m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", Environment.TickCount - DecodeTime, + AssetId); + + } else - layer.Start = layerStarts[i]; + { + m_log.WarnFormat( + "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}", + AssetId); + } + } - if (i == layerStarts.Count - 1) - layer.End = j2kdata.Length; + else + { + /* + Random rnd = new Random(); + // scramble ends for test + for (int i = 0; i < texture.LayerInfo.Length; i++) + { + texture.LayerInfo[i].End = rnd.Next(999999); + } + */ + + // Try to do some heuristics error correction! Yeah. + bool sane2Heuristics = true; + + + if (texture.Image == null) + sane2Heuristics = false; + + if (texture.LayerInfo == null) + sane2Heuristics = false; + + if (sane2Heuristics) + { + + + if (texture.LayerInfo.Length == 0) + sane2Heuristics = false; + } + + if (sane2Heuristics) + { + // Last layer start is less then the end of the file and last layer start is greater then 0 + if (texture.LayerInfo[texture.LayerInfo.Length - 1].Start < texture.AssetData.Length && texture.LayerInfo[texture.LayerInfo.Length - 1].Start > 0) + { + } + else + { + sane2Heuristics = false; + } + + } + + if (sane2Heuristics) + { + int start = 0; + + // try to fix it by using consistant data in the start field + for (int i = 0; i < texture.LayerInfo.Length; i++) + { + if (i == 0) + start = 0; + + if (i == texture.LayerInfo.Length - 1) + texture.LayerInfo[i].End = texture.AssetData.Length; + else + texture.LayerInfo[i].End = texture.LayerInfo[i + 1].Start - 1; + + // in this case, the end of the next packet is less then the start of the last packet + // after we've attempted to fix it which means the start of the last packet is borked + // there's no recovery from this + if (texture.LayerInfo[i].End < start) + { + sane2Heuristics = false; + break; + } + + if (texture.LayerInfo[i].End < 0 || texture.LayerInfo[i].End > texture.AssetData.Length) + { + sane2Heuristics = false; + break; + } + + if (texture.LayerInfo[i].Start < 0 || texture.LayerInfo[i].Start > texture.AssetData.Length) + { + sane2Heuristics = false; + break; + } + + start = texture.LayerInfo[i].Start; + } + } + + if (sane2Heuristics) + { + layers = texture.LayerInfo; + fCache.SaveFileCacheForAsset(AssetId, layers); + + + // Write out decode time + m_log.InfoFormat("[J2KDecoderModule]: HEURISTICS SUCCEEDED {0} Decode Time: {1}", Environment.TickCount - DecodeTime, + AssetId); + + } else - layer.End = layerStarts[i + 1] - 1; - - layers[i] = layer; + { + m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}. Is this a texture? is it J2K?", AssetId); + } } + texture = null; // dereference and dispose of ManagedImage + } + catch (DllNotFoundException) + { + m_log.Error( + "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!"); + OpenJpegFail = true; + } + catch (Exception ex) + { + m_log.WarnFormat( + "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}", + AssetId, ex); } } - catch (Exception ex) - { - m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + AssetId + ": " + ex.ToString()); - } + + } - if (layers.Length == 0) - { - m_log.Warn("[J2KDecoderModule]: OpenJPEG failed to decode any layer data for texture " + AssetId + ", guessing sane defaults"); - // Layer decoding completely failed. Guess at sane defaults for the layer boundaries - layers = CreateDefaultLayers(j2kdata.Length); - } + // Cache Decoded layers + lock (m_cacheddecode) + { + if (m_cacheddecode.ContainsKey(AssetId)) + m_cacheddecode.Remove(AssetId); + m_cacheddecode.Add(AssetId, layers); + + } - // Cache Decoded layers - lock (m_cacheddecode) - { - if (m_cacheddecode.ContainsKey(AssetId)) - m_cacheddecode.Remove(AssetId); - m_cacheddecode.Add(AssetId, layers); - } - } - // Notify Interested Parties lock (m_notifyList) { @@ -261,31 +377,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender } } } - - private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength) - { - OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5]; - layers[0] = new OpenJPEG.J2KLayerInfo(); - - for (int i = 0; i < layers.Length; i++) - { - OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); - - if (i == 0) - layer.Start = 0; - else - layer.Start = layers[i - 1].End + 1; - - // These default layer sizes are based on a small sampling of real-world texture data - // with extra padding thrown in for good measure. This is a worst case fallback plan - // and will probably not gracefully handle all real world data - layer.End = (int)(160d * Math.Exp(1.3d * (double)(i + 1))); - - layers[i] = layer; - } - - return layers; - } private void CleanCache() { @@ -327,9 +418,10 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender { m_cacheDecodeFolder = pFolder; m_cacheTimeout = timeout; - if (!Directory.Exists(pFolder)) + { Createj2KCacheFolder(pFolder); + } } /// @@ -355,15 +447,14 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); } - fsSWCache.Write(stringResult.ToString()); fsSWCache.Close(); fsSWCache.Dispose(); fsCache.Dispose(); - return true; } + return false; } @@ -384,9 +475,11 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender return false; if (!enabled) + { return false; + } - string readResult = String.Empty; + string readResult = string.Empty; try { @@ -400,6 +493,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender sr.Close(); sr.Dispose(); fsCachefile.Dispose(); + } catch (IOException ioe) { @@ -420,6 +514,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender "[J2KDecodeCache]: Cache Read failed. IO Exception."); } return false; + } catch (UnauthorizedAccessException) { @@ -446,6 +541,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender m_log.Error( "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!"); enabled = false; + return false; } catch (Exception e) @@ -485,6 +581,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender Layers[i] = new OpenJPEG.J2KLayerInfo(); Layers[i].Start = element1; Layers[i].End = element2; + } else { @@ -495,6 +592,9 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender } } + + + return true; } -- cgit v1.1 From f56dc5fcda16d96a309120b2a75a623cf455a8e8 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 30 Sep 2009 12:18:22 -0700 Subject: Attempting to improve the robustness of texture decoding by always ignoring LayerInfo.End values and creating guessed default layer boundaries on failed decodes Changed a noisy J2K decode log message from Info to Debug Replacing openjpeg-dotnet decoding with managed CSJ2K decoding. Should be much more reliable, faster, and use less memory * Re-added openjpeg-dotnet files since they are used elsewhere in OpenSim * Updated prebuild.xml with a reference to CSJ2K * Renamed IJ2KDecoder and J2KDecoder member names to follow standard naming conventions * Removed j2kDecodeCache cruft and replaced it with the OpenSim cache system * Rewrote the default layer boundary algorithm to use percentages instead of an exponent * Switched from an infinite in-memory cache to an expiring cache (10 minute timeout) * Slightly quieted logging errors for failed texture decodes --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 2 +- .../Agent/TextureSender/J2KDecoderModule.cs | 688 +++++---------------- .../DynamicTexture/DynamicTextureModule.cs | 2 +- OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs | 5 +- OpenSim/Region/Framework/Scenes/SceneManager.cs | 2 +- 5 files changed, 165 insertions(+), 534 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index b186720..45286be 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -351,7 +351,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); } // Send it off to the jpeg decoder - m_j2kDecodeModule.decode(m_requestedUUID, Data, J2KDecodedCallback); + m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback); } else diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs index 937f76b..49f7f48 100644 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs @@ -34,8 +34,8 @@ using System.Threading; using log4net; using Nini.Config; using OpenMetaverse; -using OpenMetaverse.Assets; using OpenMetaverse.Imaging; +using CSJ2K; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -43,31 +43,25 @@ using OpenSim.Services.Interfaces; namespace OpenSim.Region.CoreModules.Agent.TextureSender { - public delegate void J2KDecodeDelegate(UUID AssetId); + public delegate void J2KDecodeDelegate(UUID assetID); public class J2KDecoderModule : IRegionModule, IJ2KDecoder { - #region IRegionModule Members + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private static readonly ILog m_log - = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// Temporarily holds deserialized layer data information in memory + private readonly ExpiringCache m_decodedCache = new ExpiringCache(); + /// List of client methods to notify of results of decode + private readonly Dictionary> m_notifyList = new Dictionary>(); + /// Cache that will store decoded JPEG2000 layer boundary data + private IImprovedAssetCache m_cache; + /// Reference to a scene (doesn't matter which one as long as it can load the cache module) + private Scene m_scene; - /// - /// Cached Decoded Layers - /// - private readonly Dictionary m_cacheddecode = new Dictionary(); - private bool OpenJpegFail = false; - private string CacheFolder = Util.dataDir() + "/j2kDecodeCache"; - private int CacheTimeout = 720; - private J2KDecodeFileCache fCache = null; - private Thread CleanerThread = null; - private IAssetService AssetService = null; - private Scene m_Scene = null; + #region IRegionModule - /// - /// List of client methods to notify of results of decode - /// - private readonly Dictionary> m_notifyList = new Dictionary>(); + public string Name { get { return "J2KDecoderModule"; } } + public bool IsSharedModule { get { return true; } } public J2KDecoderModule() { @@ -75,630 +69,268 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender public void Initialise(Scene scene, IConfigSource source) { - if (m_Scene == null) - m_Scene = scene; - - IConfig j2kConfig = source.Configs["J2KDecoder"]; - if (j2kConfig != null) - { - CacheFolder = j2kConfig.GetString("CacheDir", CacheFolder); - CacheTimeout = j2kConfig.GetInt("CacheTimeout", CacheTimeout); - } - - if (fCache == null) - fCache = new J2KDecodeFileCache(CacheFolder, CacheTimeout); + if (m_scene == null) + m_scene = scene; scene.RegisterModuleInterface(this); - - if (CleanerThread == null && CacheTimeout != 0) - { - CleanerThread = new Thread(CleanCache); - CleanerThread.Name = "J2KCleanerThread"; - CleanerThread.IsBackground = true; - CleanerThread.Start(); - } } public void PostInitialise() { - AssetService = m_Scene.AssetService; + m_cache = m_scene.RequestModuleInterface(); } public void Close() { - - } - - public string Name - { - get { return "J2KDecoderModule"; } - } - - public bool IsSharedModule - { - get { return true; } } - #endregion + #endregion IRegionModule - #region IJ2KDecoder Members + #region IJ2KDecoder - - public void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn) + public void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback) { - // Dummy for if decoding fails. - OpenJPEG.J2KLayerInfo[] result = new OpenJPEG.J2KLayerInfo[0]; - - // Check if it's cached - bool cached = false; - lock (m_cacheddecode) - { - if (m_cacheddecode.ContainsKey(AssetId)) - { - cached = true; - result = m_cacheddecode[AssetId]; - } - } + OpenJPEG.J2KLayerInfo[] result; // If it's cached, return the cached results - if (cached) + if (m_decodedCache.TryGetValue(assetID, out result)) { - decodedReturn(AssetId, result); + callback(assetID, result); } else { - // not cached, so we need to decode it + // Not cached, we need to decode it. // Add to notify list and start decoding. // Next request for this asset while it's decoding will only be added to the notify list // once this is decoded, requests will be served from the cache and all clients in the notifylist will be updated bool decode = false; lock (m_notifyList) { - if (m_notifyList.ContainsKey(AssetId)) + if (m_notifyList.ContainsKey(assetID)) { - m_notifyList[AssetId].Add(decodedReturn); + m_notifyList[assetID].Add(callback); } else { List notifylist = new List(); - notifylist.Add(decodedReturn); - m_notifyList.Add(AssetId, notifylist); + notifylist.Add(callback); + m_notifyList.Add(assetID, notifylist); decode = true; } } + // Do Decode! if (decode) - { - doJ2kDecode(AssetId, assetData); - } + DoJ2KDecode(assetID, j2kData); } } /// /// Provides a synchronous decode so that caller can be assured that this executes before the next line /// - /// - /// - public void syncdecode(UUID AssetId, byte[] j2kdata) + /// + /// + public void Decode(UUID assetID, byte[] j2kData) { - doJ2kDecode(AssetId, j2kdata); + DoJ2KDecode(assetID, j2kData); } - #endregion + #endregion IJ2KDecoder /// /// Decode Jpeg2000 Asset Data /// - /// UUID of Asset - /// Byte Array Asset Data - private void doJ2kDecode(UUID AssetId, byte[] j2kdata) + /// UUID of Asset + /// JPEG2000 data + private void DoJ2KDecode(UUID assetID, byte[] j2kData) { int DecodeTime = 0; DecodeTime = Environment.TickCount; - OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[0]; // Dummy result for if it fails. Informs that there's only full quality + OpenJPEG.J2KLayerInfo[] layers; - if (!OpenJpegFail) + if (!TryLoadCacheForAsset(assetID, out layers)) { - if (!fCache.TryLoadCacheForAsset(AssetId, out layers)) + try { - try + List layerStarts = CSJ2K.J2kImage.GetLayerBoundaries(new MemoryStream(j2kData)); + + if (layerStarts != null && layerStarts.Count > 0) { + layers = new OpenJPEG.J2KLayerInfo[layerStarts.Count]; - AssetTexture texture = new AssetTexture(AssetId, j2kdata); - if (texture.DecodeLayerBoundaries()) + for (int i = 0; i < layerStarts.Count; i++) { - bool sane = true; - - // Sanity check all of the layers - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - if (texture.LayerInfo[i].End > texture.AssetData.Length) - { - sane = false; - break; - } - } + OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); + int start = layerStarts[i]; - if (sane) - { - layers = texture.LayerInfo; - fCache.SaveFileCacheForAsset(AssetId, layers); - - - // Write out decode time - m_log.InfoFormat("[J2KDecoderModule]: {0} Decode Time: {1}", Environment.TickCount - DecodeTime, - AssetId); - - } + if (i == 0) + layer.Start = 0; else - { - m_log.WarnFormat( - "[J2KDecoderModule]: JPEG2000 texture decoding succeeded, but sanity check failed for {0}", - AssetId); - } - } + layer.Start = layerStarts[i]; - else - { - /* - Random rnd = new Random(); - // scramble ends for test - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - texture.LayerInfo[i].End = rnd.Next(999999); - } - */ - - // Try to do some heuristics error correction! Yeah. - bool sane2Heuristics = true; - - - if (texture.Image == null) - sane2Heuristics = false; - - if (texture.LayerInfo == null) - sane2Heuristics = false; - - if (sane2Heuristics) - { - - - if (texture.LayerInfo.Length == 0) - sane2Heuristics = false; - } - - if (sane2Heuristics) - { - // Last layer start is less then the end of the file and last layer start is greater then 0 - if (texture.LayerInfo[texture.LayerInfo.Length - 1].Start < texture.AssetData.Length && texture.LayerInfo[texture.LayerInfo.Length - 1].Start > 0) - { - } - else - { - sane2Heuristics = false; - } - - } - - if (sane2Heuristics) - { - int start = 0; - - // try to fix it by using consistant data in the start field - for (int i = 0; i < texture.LayerInfo.Length; i++) - { - if (i == 0) - start = 0; - - if (i == texture.LayerInfo.Length - 1) - texture.LayerInfo[i].End = texture.AssetData.Length; - else - texture.LayerInfo[i].End = texture.LayerInfo[i + 1].Start - 1; - - // in this case, the end of the next packet is less then the start of the last packet - // after we've attempted to fix it which means the start of the last packet is borked - // there's no recovery from this - if (texture.LayerInfo[i].End < start) - { - sane2Heuristics = false; - break; - } - - if (texture.LayerInfo[i].End < 0 || texture.LayerInfo[i].End > texture.AssetData.Length) - { - sane2Heuristics = false; - break; - } - - if (texture.LayerInfo[i].Start < 0 || texture.LayerInfo[i].Start > texture.AssetData.Length) - { - sane2Heuristics = false; - break; - } - - start = texture.LayerInfo[i].Start; - } - } - - if (sane2Heuristics) - { - layers = texture.LayerInfo; - fCache.SaveFileCacheForAsset(AssetId, layers); - - - // Write out decode time - m_log.InfoFormat("[J2KDecoderModule]: HEURISTICS SUCCEEDED {0} Decode Time: {1}", Environment.TickCount - DecodeTime, - AssetId); - - } + if (i == layerStarts.Count - 1) + layer.End = j2kData.Length; else - { - m_log.WarnFormat("[J2KDecoderModule]: JPEG2000 texture decoding failed for {0}. Is this a texture? is it J2K?", AssetId); - } + layer.End = layerStarts[i + 1] - 1; + + layers[i] = layer; } - texture = null; // dereference and dispose of ManagedImage - } - catch (DllNotFoundException) - { - m_log.Error( - "[J2KDecoderModule]: OpenJpeg is not installed properly. Decoding disabled! This will slow down texture performance! Often times this is because of an old version of GLIBC. You must have version 2.4 or above!"); - OpenJpegFail = true; - } - catch (Exception ex) - { - m_log.WarnFormat( - "[J2KDecoderModule]: JPEG2000 texture decoding threw an exception for {0}, {1}", - AssetId, ex); } } - - } - - // Cache Decoded layers - lock (m_cacheddecode) - { - if (m_cacheddecode.ContainsKey(AssetId)) - m_cacheddecode.Remove(AssetId); - m_cacheddecode.Add(AssetId, layers); + catch (Exception ex) + { + m_log.Warn("[J2KDecoderModule]: CSJ2K threw an exception decoding texture " + assetID + ": " + ex.Message); + } - } + if (layers == null || layers.Length == 0) + { + m_log.Warn("[J2KDecoderModule]: Failed to decode layer data for texture " + assetID + ", guessing sane defaults"); + // Layer decoding completely failed. Guess at sane defaults for the layer boundaries + layers = CreateDefaultLayers(j2kData.Length); + } + // Cache Decoded layers + SaveFileCacheForAsset(assetID, layers); + } + // Notify Interested Parties lock (m_notifyList) { - if (m_notifyList.ContainsKey(AssetId)) + if (m_notifyList.ContainsKey(assetID)) { - foreach (DecodedCallback d in m_notifyList[AssetId]) + foreach (DecodedCallback d in m_notifyList[assetID]) { if (d != null) - d.DynamicInvoke(AssetId, layers); + d.DynamicInvoke(assetID, layers); } - m_notifyList.Remove(AssetId); + m_notifyList.Remove(assetID); } } } - - private void CleanCache() - { - m_log.Info("[J2KDecoderModule]: Cleaner thread started"); - - while (true) - { - if (AssetService != null) - fCache.ScanCacheFiles(RedecodeTexture); - System.Threading.Thread.Sleep(600000); - } - } - - private void RedecodeTexture(UUID assetID) + private OpenJPEG.J2KLayerInfo[] CreateDefaultLayers(int j2kLength) { - AssetBase texture = AssetService.Get(assetID.ToString()); - if (texture == null) - return; - - doJ2kDecode(assetID, texture.Data); + OpenJPEG.J2KLayerInfo[] layers = new OpenJPEG.J2KLayerInfo[5]; + + for (int i = 0; i < layers.Length; i++) + layers[i] = new OpenJPEG.J2KLayerInfo(); + + // These default layer sizes are based on a small sampling of real-world texture data + // with extra padding thrown in for good measure. This is a worst case fallback plan + // and may not gracefully handle all real world data + layers[0].Start = 0; + layers[1].Start = (int)((float)j2kLength * 0.02f); + layers[2].Start = (int)((float)j2kLength * 0.05f); + layers[3].Start = (int)((float)j2kLength * 0.20f); + layers[4].Start = (int)((float)j2kLength * 0.50f); + + layers[0].End = layers[1].Start - 1; + layers[1].End = layers[2].Start - 1; + layers[2].End = layers[3].Start - 1; + layers[3].End = layers[4].Start - 1; + layers[4].End = j2kLength; + + return layers; } - } - public class J2KDecodeFileCache - { - private readonly string m_cacheDecodeFolder; - private readonly int m_cacheTimeout; - private bool enabled = true; - - private static readonly ILog m_log - = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - /// - /// Creates a new instance of a file cache - /// - /// base folder for the cache. Will be created if it doesn't exist - public J2KDecodeFileCache(string pFolder, int timeout) + private void SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers) { - m_cacheDecodeFolder = pFolder; - m_cacheTimeout = timeout; - if (!Directory.Exists(pFolder)) - { - Createj2KCacheFolder(pFolder); - } - } + m_decodedCache.AddOrUpdate(AssetId, Layers, TimeSpan.FromMinutes(10)); - /// - /// Save Layers to Disk Cache - /// - /// Asset to Save the layers. Used int he file name by default - /// The Layer Data from OpenJpeg - /// - public bool SaveFileCacheForAsset(UUID AssetId, OpenJPEG.J2KLayerInfo[] Layers) - { - if (Layers.Length > 0 && enabled) + if (m_cache != null) { - FileStream fsCache = - new FileStream(String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)), - FileMode.Create); - StreamWriter fsSWCache = new StreamWriter(fsCache); + AssetBase layerDecodeAsset = new AssetBase(); + layerDecodeAsset.ID = "j2kCache_" + AssetId.ToString(); + layerDecodeAsset.Local = true; + layerDecodeAsset.Name = layerDecodeAsset.ID; + layerDecodeAsset.Temporary = true; + layerDecodeAsset.Type = (sbyte)AssetType.Notecard; + + #region Serialize Layer Data + StringBuilder stringResult = new StringBuilder(); string strEnd = "\n"; for (int i = 0; i < Layers.Length; i++) { - if (i == (Layers.Length - 1)) - strEnd = ""; + if (i == Layers.Length - 1) + strEnd = String.Empty; stringResult.AppendFormat("{0}|{1}|{2}{3}", Layers[i].Start, Layers[i].End, Layers[i].End - Layers[i].Start, strEnd); } - fsSWCache.Write(stringResult.ToString()); - fsSWCache.Close(); - fsSWCache.Dispose(); - fsCache.Dispose(); - return true; - } - - return false; - } + layerDecodeAsset.Data = Encoding.UTF8.GetBytes(stringResult.ToString()); - - /// - /// Loads the Layer data from the disk cache - /// Returns true if load succeeded - /// - /// AssetId that we're checking the cache for - /// out layers to save to - /// true if load succeeded - public bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers) - { - string filename = String.Format("{0}/{1}", m_cacheDecodeFolder, FileNameFromAssetId(AssetId)); - Layers = new OpenJPEG.J2KLayerInfo[0]; + #endregion Serialize Layer Data - if (!File.Exists(filename)) - return false; - - if (!enabled) - { - return false; + m_cache.Cache(layerDecodeAsset); } + } - string readResult = string.Empty; - - try + bool TryLoadCacheForAsset(UUID AssetId, out OpenJPEG.J2KLayerInfo[] Layers) + { + if (m_decodedCache.TryGetValue(AssetId, out Layers)) { - FileStream fsCachefile = - new FileStream(filename, - FileMode.Open); - - StreamReader sr = new StreamReader(fsCachefile); - readResult = sr.ReadToEnd(); - - sr.Close(); - sr.Dispose(); - fsCachefile.Dispose(); - + return true; } - catch (IOException ioe) + else if (m_cache != null) { - if (ioe is PathTooLongException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. Path is too long."); - } - else if (ioe is DirectoryNotFoundException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. Cache Directory does not exist!"); - enabled = false; - } - else - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. IO Exception."); - } - return false; + string assetName = "j2kCache_" + AssetId.ToString(); + AssetBase layerDecodeAsset = m_cache.Get(assetName); - } - catch (UnauthorizedAccessException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. UnauthorizedAccessException Exception. Do you have the proper permissions on this file?"); - return false; - } - catch (ArgumentException ae) - { - if (ae is ArgumentNullException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. No Filename provided"); - } - else + if (layerDecodeAsset != null) { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed. Filname was invalid"); - } - return false; - } - catch (NotSupportedException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Read failed, not supported. Cache disabled!"); - enabled = false; + #region Deserialize Layer Data - return false; - } - catch (Exception e) - { - m_log.ErrorFormat( - "[J2KDecodeCache]: Cache Read failed, unknown exception. Error: {0}", - e.ToString()); - return false; - } - - string[] lines = readResult.Split('\n'); + string readResult = Encoding.UTF8.GetString(layerDecodeAsset.Data); + string[] lines = readResult.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); - if (lines.Length <= 0) - return false; - - Layers = new OpenJPEG.J2KLayerInfo[lines.Length]; - - for (int i = 0; i < lines.Length; i++) - { - string[] elements = lines[i].Split('|'); - if (elements.Length == 3) - { - int element1, element2; - - try - { - element1 = Convert.ToInt32(elements[0]); - element2 = Convert.ToInt32(elements[1]); - } - catch (FormatException) + if (lines.Length == 0) { - m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed with ErrorConvert for {0}", AssetId); - Layers = new OpenJPEG.J2KLayerInfo[0]; + m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (empty) " + assetName); + m_cache.Expire(assetName); return false; } - Layers[i] = new OpenJPEG.J2KLayerInfo(); - Layers[i].Start = element1; - Layers[i].End = element2; - - } - else - { - // reading failed - m_log.WarnFormat("[J2KDecodeCache]: Cache Read failed for {0}", AssetId); - Layers = new OpenJPEG.J2KLayerInfo[0]; - return false; - } - } - - - - - return true; - } + Layers = new OpenJPEG.J2KLayerInfo[lines.Length]; - /// - /// Routine which converts assetid to file name - /// - /// asset id of the image - /// string filename - public string FileNameFromAssetId(UUID AssetId) - { - return String.Format("j2kCache_{0}.cache", AssetId); - } + for (int i = 0; i < lines.Length; i++) + { + string[] elements = lines[i].Split('|'); + if (elements.Length == 3) + { + int element1, element2; - public UUID AssetIdFromFileName(string fileName) - { - string rawId = fileName.Replace("j2kCache_", "").Replace(".cache", ""); - UUID asset; - if (!UUID.TryParse(rawId, out asset)) - return UUID.Zero; + try + { + element1 = Convert.ToInt32(elements[0]); + element2 = Convert.ToInt32(elements[1]); + } + catch (FormatException) + { + m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (format) " + assetName); + m_cache.Expire(assetName); + return false; + } - return asset; - } + Layers[i] = new OpenJPEG.J2KLayerInfo(); + Layers[i].Start = element1; + Layers[i].End = element2; + } + else + { + m_log.Warn("[J2KDecodeCache]: Expiring corrupted layer data (layout) " + assetName); + m_cache.Expire(assetName); + return false; + } + } - /// - /// Creates the Cache Folder - /// - /// Folder to Create - public void Createj2KCacheFolder(string pFolder) - { - try - { - Directory.CreateDirectory(pFolder); - } - catch (IOException ioe) - { - if (ioe is PathTooLongException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because the path to the cache folder is too long. Cache disabled!"); - } - else if (ioe is DirectoryNotFoundException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because the supplied base of the directory folder does not exist. Cache disabled!"); - } - else - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an IO Exception. Cache disabled!"); - } - enabled = false; + #endregion Deserialize Layer Data - } - catch (UnauthorizedAccessException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an UnauthorizedAccessException Exception. Cache disabled!"); - enabled = false; - } - catch (ArgumentException ae) - { - if (ae is ArgumentNullException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because the folder provided is invalid! Cache disabled!"); + return true; } - else - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because no cache folder was provided! Cache disabled!"); - } - enabled = false; } - catch (NotSupportedException) - { - m_log.Error( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because it's not supported. Cache disabled!"); - enabled = false; - } - catch (Exception e) - { - m_log.ErrorFormat( - "[J2KDecodeCache]: Cache Directory does not exist and create failed because of an unknown exception. Cache disabled! Error: {0}", - e.ToString()); - enabled = false; - } - } - - public void ScanCacheFiles(J2KDecodeDelegate decode) - { - DirectoryInfo dir = new DirectoryInfo(m_cacheDecodeFolder); - FileInfo[] files = dir.GetFiles("j2kCache_*.cache"); - foreach (FileInfo f in files) - { - TimeSpan fileAge = DateTime.Now - f.CreationTime; - - if (m_cacheTimeout != 0 && fileAge >= TimeSpan.FromMinutes(m_cacheTimeout)) - { - File.Delete(f.Name); - decode(AssetIdFromFileName(f.Name)); - System.Threading.Thread.Sleep(5000); - } - } + return false; } } } diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs index 14eb9a2..9a6c49a 100644 --- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs @@ -325,7 +325,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface(); if (cacheLayerDecode != null) { - cacheLayerDecode.syncdecode(asset.FullID, asset.Data); + cacheLayerDecode.Decode(asset.FullID, asset.Data); cacheLayerDecode = null; LastAssetID = asset.FullID; } diff --git a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs index b153997..856eb11 100644 --- a/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs +++ b/OpenSim/Region/Framework/Interfaces/IJ2KDecoder.cs @@ -30,12 +30,11 @@ using OpenMetaverse.Imaging; namespace OpenSim.Region.Framework.Interfaces { - public delegate void DecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers); public interface IJ2KDecoder { - void decode(UUID AssetId, byte[] assetData, DecodedCallback decodedReturn); - void syncdecode(UUID AssetId, byte[] j2kdata); + void BeginDecode(UUID assetID, byte[] j2kData, DecodedCallback callback); + void Decode(UUID assetID, byte[] j2kData); } } diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs index 0019b23..091a2d5 100644 --- a/OpenSim/Region/Framework/Scenes/SceneManager.cs +++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs @@ -652,7 +652,7 @@ namespace OpenSim.Region.Framework.Scenes AssetBase ab = sn.AssetService.Get(arrassets[i].ToString()); if (ab != null && ab.Data != null) { - j2kdecode.syncdecode(arrassets[i], ab.Data); + j2kdecode.Decode(arrassets[i], ab.Data); } } ThreadTracker.Remove(thisthread); -- cgit v1.1 From b0c0d6f9d1ebba91a45722e6f8286e81c1eed414 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 30 Sep 2009 20:35:22 +0100 Subject: Squelch the debug noise from diva's patch --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 45286be..06dd28d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -116,7 +116,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void DropAsset() { - m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID); + //m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID); m_asset = null; m_hasasset = false; m_asset_requested = false; -- cgit v1.1 From 67fefdb5a0eafc5fe899b990b096d1d70ce9d4a8 Mon Sep 17 00:00:00 2001 From: Melanie Date: Wed, 30 Sep 2009 20:52:10 +0100 Subject: Remove predecode-j2k and it's assoaciated logic --- OpenSim/Region/Application/OpenSim.cs | 20 ---- OpenSim/Region/Framework/Scenes/SceneManager.cs | 122 ------------------------ 2 files changed, 142 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 10071a0..b23cdc3 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -306,10 +306,6 @@ namespace OpenSim "delete-region ", "Delete a region from disk", RunCommand); - m_console.Commands.AddCommand("region", false, "predecode-j2k", - "predecode-j2k []>", - "Precache assets,decode j2k layerdata", RunCommand); - m_console.Commands.AddCommand("region", false, "modules list", "modules list", "List modules", HandleModules); @@ -744,22 +740,6 @@ namespace OpenSim } break; - case "predecode-j2k": - if (cmdparams.Length > 0) - { - m_sceneManager.CacheJ2kDecode(Convert.ToInt32(cmdparams[0])); - } - else - { - m_sceneManager.CacheJ2kDecode(1); - } - break; - - case "link-region": - case "unlink-region": - case "link-mapping": - HGCommands.RunHGCommand(command, cmdparams, m_sceneManager.CurrentOrFirstScene); - break; } } diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs index 091a2d5..1843e2c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneManager.cs +++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs @@ -540,127 +540,5 @@ namespace OpenSim.Region.Framework.Scenes { m_localScenes.ForEach(action); } - - public void CacheJ2kDecode(int threads) - { - if (threads < 1) threads = 1; - - IJ2KDecoder m_decoder = m_localScenes[0].RequestModuleInterface(); - - List assetRequestList = new List(); - - #region AssetGathering! - foreach (Scene scene in m_localScenes) - { - List entitles = scene.GetEntities(); - foreach (EntityBase entity in entitles) - { - if (entity is SceneObjectGroup) - { - SceneObjectGroup sog = (SceneObjectGroup) entity; - foreach (SceneObjectPart part in sog.Children.Values) - { - if (part.Shape != null) - { - if (part.Shape.TextureEntry.Length > 0) - { - OpenMetaverse.Primitive.TextureEntry te = - new Primitive.TextureEntry(part.Shape.TextureEntry, 0, - part.Shape.TextureEntry.Length); - if (te.DefaultTexture != null) // this has been null for some reason... - { - if (te.DefaultTexture.TextureID != UUID.Zero) - assetRequestList.Add(te.DefaultTexture.TextureID); - } - for (int i=0; i arrvalus = new List(); - - //split into separate arrays - for (int j = 0; j < threads; j++) - { - List val = new List(); - - for (int k = j * entries_per_thread; k < ((j + 1) * entries_per_thread); k++) - { - if (k < arrAssetRequestList.Length) - { - val.Add(arrAssetRequestList[k]); - } - - } - arrvalus.Add(val.ToArray()); - } - - for (int l = 0; l < arrvalus.Count; l++) - { - DecodeThreadContents threadworkItem = new DecodeThreadContents(); - threadworkItem.sn = m_localScenes[0]; - threadworkItem.j2kdecode = m_decoder; - threadworkItem.arrassets = arrvalus[l]; - - System.Threading.Thread decodethread = - new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(threadworkItem.run)); - - threadworkItem.SetThread(decodethread); - - decodethread.Priority = System.Threading.ThreadPriority.Lowest; - decodethread.Name = "J2kCacheDecodeThread_" + l + 1; - ThreadTracker.Add(decodethread); - decodethread.Start(); - - } - } - } - - public class DecodeThreadContents - { - public Scene sn; - public UUID[] arrassets; - public IJ2KDecoder j2kdecode; - private System.Threading.Thread thisthread; - - public void run(object o) - { - for (int i=0;i visualparams = new List(); - foreach (AgentSetAppearancePacket.VisualParamBlock x in appear.VisualParam) - { - visualparams.Add(x.ParamValue); - } + byte[] visualparams = new byte[appear.VisualParam.Length]; + for (int i = 0; i < appear.VisualParam.Length; i++) + visualparams[i] = appear.VisualParam[i].ParamValue; + + Primitive.TextureEntry te = null; + if (appear.ObjectData.TextureEntry.Length > 1) + te = new Primitive.TextureEntry(appear.ObjectData.TextureEntry, 0, appear.ObjectData.TextureEntry.Length); - handlerSetAppearance(appear.ObjectData.TextureEntry, visualparams); + handlerSetAppearance(te, visualparams); } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 23fe2d3..ea5240e 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -73,6 +73,8 @@ namespace OpenSim.Region.Framework.Scenes private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; + public static byte[] DefaultTexture; public UUID currentParcelUUID = UUID.Zero; @@ -2685,7 +2687,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// - public void SetAppearance(byte[] texture, List visualParam) + public void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams) { if (m_physicsActor != null) { @@ -2703,7 +2705,30 @@ namespace OpenSim.Region.Framework.Scenes AddToPhysicalScene(flyingTemp); } } - m_appearance.SetAppearance(texture, visualParam); + + #region Bake Cache Check + + if (textureEntry != null) + { + for (int i = 0; i < BAKE_INDICES.Length; i++) + { + int j = BAKE_INDICES[i]; + Primitive.TextureEntryFace face = textureEntry.FaceTextures[j]; + + if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + { + if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) + { + m_log.Warn("[APPEARANCE]: Missing baked texture " + face.TextureID + " (" + (AppearanceManager.TextureIndex)j + ") for avatar " + this.Name); + this.ControllingClient.SendRebakeAvatarTextures(face.TextureID); + } + } + } + } + + #endregion Bake Cache Check + + m_appearance.SetAppearance(textureEntry, visualParams); if (m_appearance.AvatarHeight > 0) SetHeight(m_appearance.AvatarHeight); m_scene.CommsManager.AvatarService.UpdateUserAppearance(m_controllingClient.AgentId, m_appearance); @@ -3253,14 +3278,14 @@ namespace OpenSim.Region.Framework.Scenes wears[i++] = new AvatarWearable(itemId, assetId); } m_appearance.Wearables = wears; - byte[] te = null; - if (cAgent.AgentTextures != null) - te = cAgent.AgentTextures; + Primitive.TextureEntry te; + if (cAgent.AgentTextures != null && cAgent.AgentTextures.Length > 1) + te = new Primitive.TextureEntry(cAgent.AgentTextures, 0, cAgent.AgentTextures.Length); else - te = AvatarAppearance.GetDefaultTexture().GetBytes(); + te = AvatarAppearance.GetDefaultTexture(); if ((cAgent.VisualParams == null) || (cAgent.VisualParams.Length < AvatarAppearance.VISUALPARAM_COUNT)) cAgent.VisualParams = AvatarAppearance.GetDefaultVisualParams(); - m_appearance.SetAppearance(te, new List(cAgent.VisualParams)); + m_appearance.SetAppearance(te, (byte[])cAgent.VisualParams.Clone()); } catch (Exception e) { diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 2316267..57f5d29 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -861,12 +861,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server Scene scene = (Scene)Scene; AvatarAppearance appearance; scene.GetAvatarAppearance(this, out appearance); - List visualParams = new List(); - foreach (byte visualParam in appearance.VisualParams) - { - visualParams.Add(visualParam); - } - OnSetAppearance(appearance.Texture.GetBytes(), visualParams); + OnSetAppearance(appearance.Texture, (byte[])appearance.VisualParams.Clone()); } public void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index b3bfe07..30a2675 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -163,13 +163,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC { AvatarAppearance x = GetAppearance(p_cloneAppearanceFrom, p_scene); - List wearbyte = new List(); - for (int i = 0; i < x.VisualParams.Length; i++) - { - wearbyte.Add(x.VisualParams[i]); - } - - sp.SetAppearance(x.Texture.GetBytes(), wearbyte); + sp.SetAppearance(x.Texture, (byte[])x.VisualParams.Clone()); } m_avatars.Add(npcAvatar.AgentId, npcAvatar); -- cgit v1.1 From 1eb6f99d19762e230c6ca905ffd99741b33db491 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Wed, 30 Sep 2009 19:42:14 -0700 Subject: Create j2k cache directory if it doesn't exist. The Meshmerizer needs it there. --- OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index f469ad6..aa8df23 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -77,6 +77,20 @@ namespace OpenSim.Region.Physics.Meshing private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh + public Meshmerizer() + { + try + { + if (!Directory.Exists(decodedScultMapPath)) + Directory.CreateDirectory(decodedScultMapPath); + } + catch (Exception e) + { + m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedScultMapPath, e.Message); + } + + } + /// /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail -- cgit v1.1 From 44776fea723774f0674881d69855af9657398d18 Mon Sep 17 00:00:00 2001 From: James J Greensky Date: Wed, 30 Sep 2009 17:30:28 -0700 Subject: Fixing LLClientView memory leak Fixing LLClientView memory leak by disposing of all timers utilized in LLClientView as they contain references to the callback method. This required the use of the Terminate and Close infrastructure that was already in place but was not being utilized. --- .../ClientStack/LindenUDP/ILLPacketHandler.cs | 3 +-- .../Region/ClientStack/LindenUDP/LLClientView.cs | 23 ++++++++++++++++++---- .../ClientStack/LindenUDP/LLPacketHandler.cs | 3 ++- .../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 8 +++++++- 4 files changed, 29 insertions(+), 8 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs index 665c773..09edc94 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs @@ -40,7 +40,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks, /// resends, pings, etc.) /// - public interface ILLPacketHandler + public interface ILLPacketHandler : IDisposable { event PacketStats OnPacketStats; event PacketDrop OnPacketDrop; @@ -70,7 +70,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP void OutPacket(Packet NewPack, ThrottleOutPacketType throttlePacketType, Object id); LLPacketQueue PacketQueue { get; } - void Stop(); void Flush(); void Clear(); ClientInfo GetClientInfo(); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 9788f40..460f94e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -633,6 +633,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // of the client thread regardless of where Close() is called. KillEndDone(); } + + Terminate(); } /// @@ -737,16 +739,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - public void Terminate() + private void Terminate() { + IsActive = false; + + m_clientPingTimer.Close(); + m_avatarTerseUpdateTimer.Close(); + m_primTerseUpdateTimer.Close(); + m_primFullUpdateTimer.Close(); + m_textureRequestTimer.Close(); + m_PacketHandler.OnPacketStats -= PopulateStats; - m_PacketHandler.Stop(); + m_PacketHandler.Dispose(); // wait for thread stoped - m_clientThread.Join(); + // m_clientThread.Join(); // delete circuit code - m_networkServer.CloseClient(this); + //m_networkServer.CloseClient(this); } #endregion @@ -876,6 +886,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP while (IsActive) { LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); + + if (nextPacket == null) { + m_log.DebugFormat("[CLIENT]: PacketQueue return null LLQueItem"); + continue; + } if (nextPacket.Incoming) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs index eaf8f60..67ece75 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs @@ -176,9 +176,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_AckTimer.Start(); } - public void Stop() + public void Dispose() { m_AckTimer.Stop(); + m_AckTimer.Close(); m_PacketQueue.Enqueue(null); m_PacketQueue.Close(); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index 798c1e7..6dd0697 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -39,7 +39,7 @@ using Timer=System.Timers.Timer; namespace OpenSim.Region.ClientStack.LindenUDP { - public class LLPacketQueue : IPullStatsProvider + public class LLPacketQueue : IPullStatsProvider, IDisposable { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -342,11 +342,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Close() { + Dispose(); + } + + public void Dispose() + { Flush(); WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby m_enabled = false; throttleTimer.Stop(); + throttleTimer.Close(); if (StatsManager.SimExtraStats != null) { -- cgit v1.1 From 5e9da4daabc49250af9c0ec810b1290c74bad885 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 1 Oct 2009 21:08:17 +0100 Subject: Add OnQueueEmpty event to the packet layers. No user functinality yet --- .../ClientStack/LindenUDP/ILLPacketHandler.cs | 6 ++- .../ClientStack/LindenUDP/LLPacketHandler.cs | 13 ++++++ .../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 49 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs index 09edc94..5658dba 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs @@ -31,9 +31,10 @@ using OpenMetaverse.Packets; using OpenSim.Framework; namespace OpenSim.Region.ClientStack.LindenUDP -{ +{ public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); public delegate void PacketDrop(Packet pack, Object id); + public delegate void QueueEmpty(ThrottleOutPacketType queue); public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType); /// @@ -44,6 +45,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { event PacketStats OnPacketStats; event PacketDrop OnPacketDrop; + event QueueEmpty OnQueueEmpty; SynchronizeClientHandler SynchronizeClient { set; } int PacketsReceived { get; } @@ -61,7 +63,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Take action depending on the type and contents of an received packet. /// - /// + /// void ProcessInPacket(LLQueItem item); void ProcessOutPacket(LLQueItem item); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs index 67ece75..801e0c5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs @@ -129,6 +129,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // public event PacketStats OnPacketStats; public event PacketDrop OnPacketDrop; + public event QueueEmpty OnQueueEmpty; //private SynchronizeClientHandler m_SynchronizeClient = null; @@ -172,6 +173,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings); + m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty; + m_AckTimer.Elapsed += AckTimerElapsed; m_AckTimer.Start(); } @@ -769,6 +772,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP handlerPacketDrop(packet, id); } + private void TriggerOnQueueEmpty(ThrottleOutPacketType queue) + { + QueueEmpty handlerQueueEmpty = OnQueueEmpty; + + if (handlerQueueEmpty == null) + return; + + handlerQueueEmpty(queue); + } + // Convert the packet to bytes and stuff it onto the send queue // public void ProcessOutPacket(LLQueItem item) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index 6dd0697..f08f1b6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -105,6 +105,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private UUID m_agentId; + public event QueueEmpty OnQueueEmpty; + public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings) { // While working on this, the BlockingQueue had me fooled for a bit. @@ -293,30 +295,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (LandOutgoingPacketQueue.Count > 0) { SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); + TriggerOnQueueEmpty(ThrottleOutPacketType.Land); } if (WindOutgoingPacketQueue.Count > 0) { SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); + TriggerOnQueueEmpty(ThrottleOutPacketType.Wind); } if (CloudOutgoingPacketQueue.Count > 0) { SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); + TriggerOnQueueEmpty(ThrottleOutPacketType.Cloud); } + bool tasksSent = false; if (TaskOutgoingPacketQueue.Count > 0) { + tasksSent = true; SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); } if (TaskLowpriorityPacketQueue.Count > 0) { + tasksSent = true; SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); } + if (tasksSent) + { + TriggerOnQueueEmpty(ThrottleOutPacketType.Task); + } if (TextureOutgoingPacketQueue.Count > 0) { SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); + TriggerOnQueueEmpty(ThrottleOutPacketType.Texture); } if (AssetOutgoingPacketQueue.Count > 0) { SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); + TriggerOnQueueEmpty(ThrottleOutPacketType.Asset); } } // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); @@ -405,6 +419,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP bool qchanged = true; ResetCounters(); + + List Empty = new List(); // m_log.Info("[THROTTLE]: Entering Throttle"); while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) { @@ -431,6 +447,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); LandThrottle.AddBytes(qpack.Length); qchanged = true; + + if (LandOutgoingPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Land); } if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) @@ -441,6 +460,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); WindThrottle.AddBytes(qpack.Length); qchanged = true; + + if (WindOutgoingPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Wind); } if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) @@ -451,6 +473,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); CloudThrottle.AddBytes(qpack.Length); qchanged = true; + + if (CloudOutgoingPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Cloud); } if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) @@ -470,6 +495,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); TaskThrottle.AddBytes(qpack.Length); qchanged = true; + + if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Task); } if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) @@ -480,6 +508,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); TextureThrottle.AddBytes(qpack.Length); qchanged = true; + + if (TextureOutgoingPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Texture); } if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) @@ -490,12 +521,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.AddBytes(qpack.Length); AssetThrottle.AddBytes(qpack.Length); qchanged = true; + + if (AssetOutgoingPacketQueue.Count == 0) + Empty.Add(ThrottleOutPacketType.Asset); } } // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); + + foreach (ThrottleOutPacketType t in Empty) + { + TriggerOnQueueEmpty(t); + } } } + private void TriggerOnQueueEmpty(ThrottleOutPacketType queue) + { + QueueEmpty handlerQueueEmpty = OnQueueEmpty; + + if (handlerQueueEmpty == null) + return; + + handlerQueueEmpty(queue); + } + private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) { // just to change the signature, and that ProcessThrottle -- cgit v1.1 From 54a912bb9cd5148abfa5eb68b0146ae3b04051a3 Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 1 Oct 2009 22:35:57 +0100 Subject: Add a method to determine the count of packets in a throttle --- .../ClientStack/LindenUDP/ILLPacketHandler.cs | 1 + .../Region/ClientStack/LindenUDP/LLPacketHandler.cs | 5 +++++ .../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs index 5658dba..31f9580 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs @@ -78,5 +78,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP void SetClientInfo(ClientInfo info); void AddImportantPacket(PacketType type); void RemoveImportantPacket(PacketType type); + int GetQueueCount(ThrottleOutPacketType queue); } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs index 801e0c5..37f6ca7 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs @@ -863,5 +863,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_PacketQueue.Close(); Thread.CurrentThread.Abort(); } + + public int GetQueueCount(ThrottleOutPacketType queue) + { + return m_PacketQueue.GetQueueCount(queue); + } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index f08f1b6..d4d654f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -753,5 +753,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP { get { return throttleMultiplier; } } + + public int GetQueueCount(ThrottleOutPacketType queue) + { + switch (queue) + { + case ThrottleOutPacketType.Land: + return LandOutgoingPacketQueue.Count; + case ThrottleOutPacketType.Wind: + return WindOutgoingPacketQueue.Count; + case ThrottleOutPacketType.Cloud: + return CloudOutgoingPacketQueue.Count; + case ThrottleOutPacketType.Task: + return TaskOutgoingPacketQueue.Count; + case ThrottleOutPacketType.Texture: + return TextureOutgoingPacketQueue.Count; + case ThrottleOutPacketType.Asset: + return AssetOutgoingPacketQueue.Count; + } + + return 0; + } } } -- cgit v1.1 From 6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 1 Oct 2009 17:42:13 -0700 Subject: * 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) --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 455 ++++++++++----------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 324 ++++++--------- .../Agent/TextureSender/J2KDecoderModule.cs | 1 - 3 files changed, 331 insertions(+), 449 deletions(-) (limited to 'OpenSim/Region') 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; namespace OpenSim.Region.ClientStack.LindenUDP { /// - /// We use this class to store image data and associated request data and attributes + /// Stores information about a current texture download and a reference to the texture asset /// public class J2KImage { + private const int IMAGE_PACKET_SIZE = 1000; + private const int FIRST_PACKET_SIZE = 600; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public double m_designatedPriorityKey; - public double m_requestedPriority = 0.0d; - public uint m_lastSequence = 0; + public uint m_lastSequence; + public float m_requestedPriority; public uint m_requestedPacketNumber; public sbyte m_requestedDiscardLevel; public UUID m_requestedUUID; public IJ2KDecoder m_j2kDecodeModule; public IAssetService m_assetCache; - public OpenJPEG.J2KLayerInfo[] Layers = new OpenJPEG.J2KLayerInfo[0]; - public AssetBase m_MissingSubstitute = null; - public bool m_decoded = false; - public bool m_completedSendAtCurrentDiscardLevel; + public OpenJPEG.J2KLayerInfo[] m_layers; + public bool m_decoded; + public bool m_hasasset; + public C5.IPriorityQueueHandle m_priorityQueueHandle; - private sbyte m_discardLevel=-1; private uint m_packetNumber; - private bool m_decoderequested = false; - public bool m_hasasset = false; - private bool m_asset_requested = false; - private bool m_sentinfo = false; - private uint m_stopPacket = 0; - private const int cImagePacketSize = 1000; - private const int cFirstPacketSize = 600; - - private AssetBase m_asset = null; - private int m_assetDataLength = 0; - - private LLImageManager m_image; + private bool m_decoderequested; + private bool m_asset_requested; + private bool m_sentinfo; + private uint m_stopPacket; + private AssetBase m_asset; + private int m_assetDataLength; + private LLImageManager m_imageManager; - public J2KImage(LLImageManager image) - { - m_image = image; - } + #region Properties public uint m_pPacketNumber { @@ -88,10 +81,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP public byte[] Data { - get - { - if (m_asset != null) - return m_asset.Data; + get + { + if (m_asset != null) + return m_asset.Data; else return null; } @@ -101,9 +94,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (!m_decoded) return 0; + try { - return (ushort)(((m_assetDataLength - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1); + return (ushort)(((m_assetDataLength - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1); } catch (Exception) { @@ -114,127 +108,145 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void DropAsset() - { - //m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID); - m_asset = null; - m_hasasset = false; - m_asset_requested = false; - } + #endregion Properties - public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) + public J2KImage(LLImageManager imageManager) { - m_image.m_outstandingtextures++; - Layers = layers; - m_decoded = true; - RunUpdate(); + m_imageManager = imageManager; } - public void AssetDataCallback(UUID AssetID, AssetBase asset) + public bool SendPackets(LLClientView client, int maxpack) { - m_hasasset = true; - if (asset == null || asset.Data == null) - { - m_asset = m_MissingSubstitute; - } - else + if (m_packetNumber <= m_stopPacket) { - m_asset = asset; - } - - m_assetDataLength = m_asset.Data.Length; - - RunUpdate(); - } - - protected void AssetReceived(string id, Object sender, AssetBase asset) - { - UUID assetID = UUID.Zero; - if (asset != null) - assetID = asset.FullID; - - AssetDataCallback(assetID, asset); - - } + bool SendMore = true; + if (!m_sentinfo || (m_packetNumber == 0)) + { + if (SendFirstPacket(client)) + { + SendMore = false; + } + m_sentinfo = true; + m_packetNumber++; + } + // bool ignoreStop = false; + if (m_packetNumber < 2) + { + m_packetNumber = 2; + } - private int GetPacketForBytePosition(int bytePosition) - { - return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1; - } + int count = 0; + while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket) + { + count++; + SendMore = SendPacket(client); + m_packetNumber++; + } - public int LastPacketSize() - { - if (m_packetNumber == 1) - return m_assetDataLength; - int lastsize = (m_assetDataLength - cFirstPacketSize) % cImagePacketSize; - //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary - if (lastsize == 0) - { - lastsize = cImagePacketSize; + if (m_packetNumber > m_stopPacket) + return true; } - return lastsize; - } - - public int CurrentBytePosition() - { - if (m_packetNumber == 0) - return 0; - if (m_packetNumber == 1) - return cFirstPacketSize; - int result = cFirstPacketSize + ((int)m_packetNumber - 2) * cImagePacketSize; - if (result < 0) - { - result = cFirstPacketSize; - } - return result; + return false; } - public bool SendFirstPacket(LLClientView client) + public void RunUpdate() { - // this means we don't have - if (Data == null) - { - client.SendImageNotFound(m_requestedUUID); - m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); - return true; - } - // Do we have less then 1 packet's worth of data? - else if (m_assetDataLength <= cFirstPacketSize) + //This is where we decide what we need to update + //and assign the real discardLevel and packetNumber + //assuming of course that the connected client might be bonkers + + if (!m_hasasset) { - // Send only 1 packet - client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2); - m_stopPacket = 0; - return true; + if (!m_asset_requested) + { + m_asset_requested = true; + m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); + } } else { - byte[] firstImageData = new byte[cFirstPacketSize]; - try - { - Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); - client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2); + if (!m_decoded) + { + //We need to decode the requested image first + if (!m_decoderequested) + { + //Request decode + m_decoderequested = true; + // Do we have a jpeg decoder? + if (m_j2kDecodeModule != null) + { + if (Data == null) + { + J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); + } + else + { + // Send it off to the jpeg decoder + m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback); + } + + } + else + { + J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); + } + } } - catch (Exception) + else { - m_log.Error("Texture block copy failed. Possibly out of memory?"); - return true; + // Check for missing image asset data + if (m_asset == null || m_asset.Data == null) + { + // FIXME: + m_packetNumber = m_stopPacket; + return; + } + + if (m_requestedDiscardLevel >= 0 || m_stopPacket == 0) + { + int maxDiscardLevel = Math.Max(0, m_layers.Length - 1); + + // Treat initial texture downloads with a DiscardLevel of -1 a request for the highest DiscardLevel + if (m_requestedDiscardLevel < 0 && m_stopPacket == 0) + m_requestedDiscardLevel = (sbyte)maxDiscardLevel; + + // Clamp at the highest discard level + m_requestedDiscardLevel = (sbyte)Math.Min(m_requestedDiscardLevel, maxDiscardLevel); + + //Calculate the m_stopPacket + if (m_layers.Length > 0) + { + m_stopPacket = (uint)GetPacketForBytePosition(m_layers[(m_layers.Length - 1) - m_requestedDiscardLevel].End); + //I don't know why, but the viewer seems to expect the final packet if the file + //is just one packet bigger. + if (TexturePacketCount() == m_stopPacket + 1) + { + m_stopPacket = TexturePacketCount(); + } + } + else + { + m_stopPacket = TexturePacketCount(); + } + + m_packetNumber = m_requestedPacketNumber; + } } } - return false; } private bool SendPacket(LLClientView client) { bool complete = false; - int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize; + int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE; try { - if ((CurrentBytePosition() + cImagePacketSize) > m_assetDataLength) + if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_assetDataLength) { imagePacketSize = LastPacketSize(); - complete=true; + complete = true; if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength) { imagePacketSize = m_assetDataLength - CurrentBytePosition(); @@ -259,7 +271,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //Send the packet - client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData); + client.SendImageNextPart((ushort)(m_packetNumber - 1), m_requestedUUID, imageData); } if (complete) { @@ -275,146 +287,107 @@ namespace OpenSim.Region.ClientStack.LindenUDP return false; } } - public bool SendPackets(LLClientView client, int maxpack) + + private int GetPacketForBytePosition(int bytePosition) { + return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1; + } - if (!m_completedSendAtCurrentDiscardLevel) + private int LastPacketSize() + { + if (m_packetNumber == 1) + return m_assetDataLength; + int lastsize = (m_assetDataLength - FIRST_PACKET_SIZE) % IMAGE_PACKET_SIZE; + //If the last packet size is zero, it's really cImagePacketSize, it sits on the boundary + if (lastsize == 0) { - if (m_packetNumber <= m_stopPacket) - { - bool SendMore = true; - if (!m_sentinfo || (m_packetNumber == 0)) - { - if (SendFirstPacket(client)) - { - SendMore = false; - } - m_sentinfo = true; - m_packetNumber++; - } - // bool ignoreStop = false; - if (m_packetNumber < 2) - { - m_packetNumber = 2; - } - - int count = 0; - while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket) - { - count++; - SendMore = SendPacket(client); - m_packetNumber++; - } - - if (m_packetNumber > m_stopPacket) - { - return true; - } - } + lastsize = IMAGE_PACKET_SIZE; } - return false; + return lastsize; } - public void RunUpdate() + private int CurrentBytePosition() { - //This is where we decide what we need to update - //and assign the real discardLevel and packetNumber - //assuming of course that the connected client might be bonkers + if (m_packetNumber == 0) + return 0; + if (m_packetNumber == 1) + return FIRST_PACKET_SIZE; - if (!m_hasasset) + int result = FIRST_PACKET_SIZE + ((int)m_packetNumber - 2) * IMAGE_PACKET_SIZE; + if (result < 0) { + result = FIRST_PACKET_SIZE; + } + return result; + } - if (!m_asset_requested) + private bool SendFirstPacket(LLClientView client) + { + // this means we don't have + if (Data == null) + { + client.SendImageNotFound(m_requestedUUID); + m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); + return true; + } + // Do we have less then 1 packet's worth of data? + else if (m_assetDataLength <= FIRST_PACKET_SIZE) + { + // Send only 1 packet + client.SendImageFirstPart(1, m_requestedUUID, (uint)m_assetDataLength, m_asset.Data, 2); + m_stopPacket = 0; + return true; + } + else + { + byte[] firstImageData = new byte[FIRST_PACKET_SIZE]; + try { - m_asset_requested = true; - m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); - + Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)FIRST_PACKET_SIZE); + client.SendImageFirstPart(TexturePacketCount(), m_requestedUUID, (uint)m_assetDataLength, firstImageData, 2); + } + catch (Exception) + { + m_log.Error("Texture block copy failed. Possibly out of memory?"); + return true; } + } + return false; + } + + private void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) + { + m_layers = layers; + m_decoded = true; + RunUpdate(); + } + + private void AssetDataCallback(UUID AssetID, AssetBase asset) + { + m_hasasset = true; + if (asset == null || asset.Data == null) + { + m_asset = null; + m_decoded = true; } else { + m_asset = asset; + m_assetDataLength = m_asset.Data.Length; + } + RunUpdate(); + } - if (!m_decoded) - { - //We need to decode the requested image first - if (!m_decoderequested) - { - //Request decode - m_decoderequested = true; - // Do we have a jpeg decoder? - if (m_j2kDecodeModule != null) - { - if (Data == null) - { - J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); - } - else - { - // Send it off to the jpeg decoder - m_j2kDecodeModule.BeginDecode(m_requestedUUID, Data, J2KDecodedCallback); - } - - } - else - { - J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); - } - } - - } - else - { - //discardLevel of -1 means just update the priority - if (m_requestedDiscardLevel != -1) - { - //Evaluate the discard level - //First, is it positive? - if (m_requestedDiscardLevel >= 0) - { - if (m_requestedDiscardLevel > Layers.Length - 1) - { - m_discardLevel = (sbyte)(Layers.Length - 1); - } - else - { - m_discardLevel = m_requestedDiscardLevel; - } + private void AssetReceived(string id, Object sender, AssetBase asset) + { + UUID assetID = UUID.Zero; + if (asset != null) + assetID = asset.FullID; - //Calculate the m_stopPacket - if (Layers.Length > 0) - { - m_stopPacket = (uint)GetPacketForBytePosition(Layers[(Layers.Length - 1) - m_discardLevel].End); - //I don't know why, but the viewer seems to expect the final packet if the file - //is just one packet bigger. - if (TexturePacketCount() == m_stopPacket + 1) - { - m_stopPacket = TexturePacketCount(); - } - } - else - { - m_stopPacket = TexturePacketCount(); - } - //Don't reset packet number unless we're waiting or it's ahead of us - if (m_completedSendAtCurrentDiscardLevel || m_requestedPacketNumber>m_packetNumber) - { - m_packetNumber = m_requestedPacketNumber; - } + AssetDataCallback(assetID, asset); - if (m_packetNumber <= m_stopPacket) - { - m_completedSendAtCurrentDiscardLevel = false; - } - } - } - else - { - m_packetNumber = m_stopPacket; - } - } - } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index b039049..a82eaae 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -27,26 +27,28 @@ using System; using System.Threading; +using System.Collections; using System.Collections.Generic; +using System.Reflection; using OpenMetaverse; using OpenMetaverse.Imaging; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Services.Interfaces; using log4net; -using System.Reflection; namespace OpenSim.Region.ClientStack.LindenUDP { - public class LLImageManager { - - //Public interfaces: - //Constructor - (LLClientView, IAssetCache, IJ2KDecoder); - //void EnqueueReq - (TextureRequestArgs) - //ProcessImageQueue - //Close + private sealed class J2KImageComparer : IComparer + { + public int Compare(J2KImage x, J2KImage y) + { + return x.m_requestedPriority.CompareTo(y.m_requestedPriority); + } + } + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool m_shuttingdown = false; private long m_lastloopprocessed = 0; @@ -54,28 +56,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP private LLClientView m_client; //Client we're assigned to private IAssetService m_assetCache; //Asset Cache private IJ2KDecoder m_j2kDecodeModule; //Our J2K module + private C5.IntervalHeap m_priorityQueue = new C5.IntervalHeap(10, new J2KImageComparer()); - private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes - private Dictionary m_imagestore; // Our main image storage dictionary - private SortedList m_priorities; // For fast image lookup based on priority - private Dictionary m_priorityresolver; //Enabling super fast assignment of images with the same priorities - - private const double doubleMinimum = .0000001; - - public int m_outstandingtextures = 0; - //Constructor public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule) { - - m_imagestore = new Dictionary(); - m_priorities = new SortedList(); - m_priorityresolver = new Dictionary(); m_client = client; m_assetCache = pAssetCache; - if (pAssetCache != null) - m_missingsubstitute = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); - else - m_log.Error("[ClientView] - couldn't set missing image, all manner of things will probably break"); m_j2kDecodeModule = pJ2kDecodeModule; } @@ -88,174 +74,147 @@ namespace OpenSim.Region.ClientStack.LindenUDP //Make sure we're not shutting down.. if (!m_shuttingdown) { + J2KImage imgrequest; - //Do we already know about this UUID? - if (m_imagestore.ContainsKey(newRequest.RequestedAssetID)) - { - //Check the packet sequence to make sure this isn't older than - //one we've already received - - J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID]; + // Do a linear search for this texture download + m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest); - // This is the inbound request sequence number. We can ignore - // "old" ones. - - if (newRequest.requestSequence > imgrequest.m_lastSequence) + if (imgrequest != null) + { + if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) { + m_log.Debug("[JPEG2000]: (CAN) ID=" + newRequest.RequestedAssetID); - imgrequest.m_lastSequence = newRequest.requestSequence; - - //Check the priority + try { m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle); } + catch (Exception) { } + } + else + { + m_log.DebugFormat("[JPEG2000]: (UPD) ID={0}: D={1}, S={2}, P={3}", + newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); - double priority = imgrequest.m_requestedPriority; - if (priority != newRequest.Priority) + //Check the packet sequence to make sure this isn't older than + //one we've already received + if (newRequest.requestSequence > imgrequest.m_lastSequence) { - //Remove the old priority - m_priorities.Remove(imgrequest.m_designatedPriorityKey); - //Assign a new unique priority - imgrequest.m_requestedPriority = newRequest.Priority; - imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); - } + //Update the sequence number of the last RequestImage packet + imgrequest.m_lastSequence = newRequest.requestSequence; - //Update the requested discard level - imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; + //Update the requested discard level + imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; - //Update the requested packet number - imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; + //Update the requested packet number + imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; - //Check if this will create an outstanding texture request - bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel; - //Run an update - - imgrequest.RunUpdate(); + //Update the requested priority + imgrequest.m_requestedPriority = newRequest.Priority; + try { m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest); } + catch (Exception) { imgrequest.m_priorityQueueHandle = null; m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); } - if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) - { - Interlocked.Increment(ref m_outstandingtextures); + //Run an update + imgrequest.RunUpdate(); } } } else { - J2KImage imgrequest = new J2KImage(this); - - //Assign our missing substitute - imgrequest.m_MissingSubstitute = m_missingsubstitute; + if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) + { + m_log.DebugFormat("[JPEG2000]: (IGN) ID={0}: D={1}, S={2}, P={3}", + newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); + } + else + { + m_log.DebugFormat("[JPEG2000]: (NEW) ID={0}: D={1}, S={2}, P={3}", + newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); - //Assign our decoder module - imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; + imgrequest = new J2KImage(this); - //Assign our asset cache module - imgrequest.m_assetCache = m_assetCache; + //Assign our decoder module + imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; - //Assign a priority based on our request - imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); + //Assign our asset cache module + imgrequest.m_assetCache = m_assetCache; - //Assign the requested discard level - imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; + //Assign the requested discard level + imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; - //Assign the requested packet number - imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; + //Assign the requested packet number + imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; - //Assign the requested priority - imgrequest.m_requestedPriority = newRequest.Priority; + //Assign the requested priority + imgrequest.m_requestedPriority = newRequest.Priority; - //Assign the asset uuid - imgrequest.m_requestedUUID = newRequest.RequestedAssetID; + //Assign the asset uuid + imgrequest.m_requestedUUID = newRequest.RequestedAssetID; - m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest); + //Assign the requested priority + imgrequest.m_requestedPriority = newRequest.Priority; - //Run an update - imgrequest.RunUpdate(); + //Add this download to the priority queue + m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); + //Run an update + imgrequest.RunUpdate(); + } } } } - private double AssignPriority(UUID pAssetID, double pPriority) + public bool ProcessImageQueue(int count, int maxpack) { - - //First, find out if we can just assign directly - if (m_priorityresolver.ContainsKey((int)pPriority) == false) + //count is the number of textures we want to process in one go. + //As part of this class re-write, that number will probably rise + //since we're processing in a more efficient manner. + + // this can happen during Close() + if (m_client == null) + return false; + + int numCollected = 0; + + //Calculate our threshold + int threshold; + if (m_lastloopprocessed == 0) { - m_priorities.Add((double)((int)pPriority), pAssetID); - m_priorityresolver.Add((int)pPriority, 0); - return (double)((int)pPriority); + if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) + return false; + //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below + threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; + m_lastloopprocessed = DateTime.Now.Ticks; } else { - //Use the hash lookup goodness of a secondary dictionary to find a free slot - double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1)); - m_priorities[mFreePriority] = pAssetID; - m_priorityresolver[(int)pPriority]++; - return mFreePriority; - } + double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; + throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; + //Average of 1000 bytes per packet + throttleseconds = throttleseconds / 1000; + //Safe-zone multiplier of 2.0 + threshold = (int)(throttleseconds * 2.0); + m_lastloopprocessed = DateTime.Now.Ticks; - } + } - public bool ProcessImageQueue(int count, int maxpack) - { + if (m_client.PacketHandler == null) + return false; - // this can happen during Close() - if (m_client == null) + if (m_client.PacketHandler.PacketQueue == null) return false; - - //Count is the number of textures we want to process in one go. - //As part of this class re-write, that number will probably rise - //since we're processing in a more efficient manner. - - int numCollected = 0; - //Calculate our threshold - int threshold; - if (m_lastloopprocessed == 0) - { - if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) - return false; - //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below - threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; - m_lastloopprocessed = DateTime.Now.Ticks; - } - else - { - double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; - throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; - - //Average of 1000 bytes per packet - throttleseconds = throttleseconds / 1000; - - //Safe-zone multiplier of 2.0 - threshold = (int)(throttleseconds * 2.0); - m_lastloopprocessed = DateTime.Now.Ticks; - - } - - if (threshold < 10) - { - threshold = 10; - } - - if (m_client.PacketHandler == null) - return false; - - if (m_client.PacketHandler.PacketQueue == null) - return false; - - //First of all make sure our packet queue isn't above our threshold + if (threshold < 10) + threshold = 10; //Uncomment this to see what the texture stack is doing - //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); - if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold && m_outstandingtextures > 0) - { - bool justreset = false; - - for (int x = m_priorities.Count - 1; x > -1; x--) + //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); + if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold) + { + while (m_priorityQueue.Count > 0) { - - J2KImage imagereq = m_imagestore[m_priorities.Values[x]]; - if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel) + J2KImage imagereq = m_priorityQueue.FindMax(); + + if (imagereq.m_decoded == true) { // we need to test this here now that we are dropping assets if (!imagereq.m_hasasset) @@ -265,78 +224,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP continue; } - numCollected++; + ++numCollected; + //SendPackets will send up to ten packets per cycle if (imagereq.SendPackets(m_client, maxpack)) { - //Send complete - if (!imagereq.m_completedSendAtCurrentDiscardLevel) - { - // I think this field imagereq.m_completedSendAtCurrentDiscardLevel - // is completely redundant - //imagereq.m_completedSendAtCurrentDiscardLevel = true; - - Interlocked.Decrement(ref m_outstandingtextures); - - // First and foremost, drop the reference to the asset - // so that the asset doesn't stay in memory forever. - // We'll Get it again from the asset service (usually cache) - // if/when the client requests it again. - // In order not to mess much with the current implementation - // of this management code, we drop only the asset reference - // but keep the image request itself. - imagereq.DropAsset(); - - //Re-assign priority to bottom - //Remove the old priority - m_priorities.Remove(imagereq.m_designatedPriorityKey); - int lowest; - if (m_priorities.Count > 0) - { - lowest = (int)m_priorities.Keys[0]; - lowest--; - } - else - { - lowest = -10000; - } - m_priorities.Add((double)lowest, imagereq.m_requestedUUID); - imagereq.m_designatedPriorityKey = (double)lowest; - if (m_priorityresolver.ContainsKey((int)lowest)) - { - m_priorityresolver[(int)lowest]++; - } - else - { - m_priorityresolver.Add((int)lowest, 0); - } - } - } - if (numCollected == count) - { - break; + // Send complete. Destroy any knowledge of this transfer + try { m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); } + catch (Exception) { } } } - if (numCollected == count || m_outstandingtextures == 0) - break; - if (numCollected % m_outstandingtextures == 0 && !justreset) - { - //We've gotten as much as we can from the stack, - //reset to the top so that we can send MOAR DATA (nomnomnom)! - x = m_priorities.Count - 1; - justreset = true; //prevents us from getting stuck in a loop - } + if (numCollected == count) + break; } } - return m_outstandingtextures != 0; + return m_priorityQueue.Count > 0; } //Faux destructor public void Close() { - + m_shuttingdown = true; m_j2kDecodeModule = null; m_assetCache = null; diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs index 49f7f48..a0f359b 100644 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs @@ -161,7 +161,6 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender for (int i = 0; i < layerStarts.Count; i++) { OpenJPEG.J2KLayerInfo layer = new OpenJPEG.J2KLayerInfo(); - int start = layerStarts[i]; if (i == 0) layer.Start = 0; -- cgit v1.1 From 54ff84050fbd776c3d5dec8bada70b17934dc291 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 1 Oct 2009 18:12:13 -0700 Subject: Removing console debug spew --- OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index a82eaae..70af8e8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -83,15 +83,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) { - m_log.Debug("[JPEG2000]: (CAN) ID=" + newRequest.RequestedAssetID); + //m_log.Debug("[TEX]: (CAN) ID=" + newRequest.RequestedAssetID); try { m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle); } catch (Exception) { } } else { - m_log.DebugFormat("[JPEG2000]: (UPD) ID={0}: D={1}, S={2}, P={3}", - newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); + //m_log.DebugFormat("[TEX]: (UPD) ID={0}: D={1}, S={2}, P={3}", + // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); //Check the packet sequence to make sure this isn't older than //one we've already received @@ -120,13 +120,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) { - m_log.DebugFormat("[JPEG2000]: (IGN) ID={0}: D={1}, S={2}, P={3}", - newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); + //m_log.DebugFormat("[TEX]: (IGN) ID={0}: D={1}, S={2}, P={3}", + // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); } else { - m_log.DebugFormat("[JPEG2000]: (NEW) ID={0}: D={1}, S={2}, P={3}", - newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); + //m_log.DebugFormat("[TEX]: (NEW) ID={0}: D={1}, S={2}, P={3}", + // newRequest.RequestedAssetID, newRequest.DiscardLevel, newRequest.PacketNumber, newRequest.Priority); imgrequest = new J2KImage(this); -- cgit v1.1 From 18a744cac10c797b67236988a84ab6d13a5543b8 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 2 Oct 2009 04:04:14 +0100 Subject: Change texture sending to be driven by the queue empty event from the packet queue, rather than a timer --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 6 ++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 71 ++++++---------------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 9 ++- .../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 64 +++++++++---------- 4 files changed, 60 insertions(+), 90 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 1448722..b958b01 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -232,6 +232,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetNumber = m_requestedPacketNumber; } + + if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0) + { + m_log.Debug("No textures queued, sending one packet to kickstart it"); + SendPacket(m_imageManager.Client); + } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 460f94e..23fa1a3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -81,8 +81,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private List m_primFullUpdates = new List(); - private Timer m_textureRequestTimer; - private bool m_clientBlocked; private int m_probesWithNoIngressPackets; @@ -143,9 +141,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_primTerseUpdateRate = 10; protected int m_primFullUpdateRate = 14; - protected int m_textureRequestRate = 100; - protected int m_textureSendLimit = 10; - protected int m_textureDataLimit = 5; + protected int m_textureSendLimit = 100; + protected int m_textureDataLimit = 10; protected int m_packetMTU = 1400; @@ -534,6 +531,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_PacketHandler = new LLPacketHandler(this, m_networkServer, userSettings); m_PacketHandler.SynchronizeClient = SynchronizeClient; m_PacketHandler.OnPacketStats += PopulateStats; + m_PacketHandler.OnQueueEmpty += HandleQueueEmpty; if (scene.Config != null) { @@ -555,9 +553,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate", m_primFullUpdateRate); - m_textureRequestRate = clientConfig.GetInt("TextureRequestRate", - m_textureRequestRate); - m_textureSendLimit = clientConfig.GetInt("TextureSendLimit", m_textureSendLimit); @@ -607,9 +602,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_primFullUpdateTimer.Enabled) lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); - if (m_textureRequestTimer.Enabled) - lock (m_textureRequestTimer) - m_textureRequestTimer.Stop(); // This is just to give the client a reasonable chance of // flushing out all it's packets. There should probably @@ -706,10 +698,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_primFullUpdateTimer.Enabled) lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); - - if (m_textureRequestTimer.Enabled) - lock (m_textureRequestTimer) - m_textureRequestTimer.Stop(); } public void Restart() @@ -732,11 +720,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); m_primFullUpdateTimer.AutoReset = false; - - m_textureRequestTimer = new Timer(m_textureRequestRate); - m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests); - m_textureRequestTimer.AutoReset = false; - } private void Terminate() @@ -747,7 +730,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_avatarTerseUpdateTimer.Close(); m_primTerseUpdateTimer.Close(); m_primFullUpdateTimer.Close(); - m_textureRequestTimer.Close(); m_PacketHandler.OnPacketStats -= PopulateStats; m_PacketHandler.Dispose(); @@ -982,10 +964,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); m_primFullUpdateTimer.AutoReset = false; - m_textureRequestTimer = new Timer(m_textureRequestRate); - m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests); - m_textureRequestTimer.AutoReset = false; - m_scene.AddNewClient(this); RefreshGroupMembership(); @@ -1057,26 +1035,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - protected virtual void TextureRequestHandler() - { - m_log.DebugFormat("[TRH] Thread started"); - while (m_imageManager != null) - { - try - { - while (m_imageManager != null) - { - } - } - catch (Exception e) - { - m_log.WarnFormat("[TRH] Exception in handler loop: {0}", e.Message); - m_log.Debug(e); - } - } - m_log.DebugFormat("[TRH] Thread terminated"); - } - # endregion // Previously ClientView.API partial class @@ -3176,16 +3134,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Unlike the other timers, this one is only started after // the first request is seen. - void ProcessTextureRequests(object sender, ElapsedEventArgs e) + void HandleQueueEmpty(ThrottleOutPacketType queue) + { + switch (queue) + { + case ThrottleOutPacketType.Texture: + m_log.Debug("Texture queue empty"); + ProcessTextureRequests(); + break; + } + } + + void ProcessTextureRequests() { if (m_imageManager != null) { - if (m_imageManager.ProcessImageQueue(m_textureSendLimit, - m_textureDataLimit)) - { - lock (m_textureRequestTimer) - m_textureRequestTimer.Start(); - } + m_imageManager.ProcessImageQueue(m_textureSendLimit, + m_textureDataLimit); } } @@ -6638,8 +6603,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_imageManager != null) { m_imageManager.EnqueueReq(args); - lock (m_textureRequestTimer) - m_textureRequestTimer.Start(); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 70af8e8..1556d01 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -65,6 +65,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_j2kDecodeModule = pJ2kDecodeModule; } + public LLClientView Client + { + get { return m_client; } + } + public void EnqueueReq(TextureRequestArgs newRequest) { //newRequest is the properties of our new texture fetch request. @@ -207,8 +212,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP threshold = 10; //Uncomment this to see what the texture stack is doing - //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); - if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold) + //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); + if (m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold) { while (m_priorityQueue.Count > 0) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index d4d654f..8484846 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -62,6 +62,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Queue TextureOutgoingPacketQueue; private Queue AssetOutgoingPacketQueue; + private List Empty = new List(); + // m_log.Info("[THROTTLE]: Entering Throttle"); // private Dictionary PendingAcks = new Dictionary(); // private Dictionary NeedAck = new Dictionary(); @@ -85,20 +87,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Dictionary contents = new Dictionary(); - /// - /// The number of packets in the OutgoingPacketQueue - /// - /// - internal int TextureOutgoingPacketQueueCount - { - get - { - if (TextureOutgoingPacketQueue == null) - return 0; - return TextureOutgoingPacketQueue.Count; - } - } - // private long LastThrottle; // private long ThrottleInterval; private Timer throttleTimer; @@ -212,28 +200,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP switch (item.throttleType & ThrottleOutPacketType.TypeMask) { case ThrottleOutPacketType.Resend: - ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item); + ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend); break; case ThrottleOutPacketType.Texture: - ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item); + ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture); break; case ThrottleOutPacketType.Task: if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0) - ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item); + ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task); else - ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); + ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task); break; case ThrottleOutPacketType.Land: - ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); + ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land); break; case ThrottleOutPacketType.Asset: - ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item); + ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset); break; case ThrottleOutPacketType.Cloud: - ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item); + ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud); break; case ThrottleOutPacketType.Wind: - ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item); + ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind); break; default: @@ -408,6 +396,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. int throttleLoops = 0; + List e; // We're going to dequeue all of the saved up packets until // we've hit the throttle limit or there's no more packets to send @@ -420,8 +409,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP ResetCounters(); - List Empty = new List(); - // m_log.Info("[THROTTLE]: Entering Throttle"); while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) { qchanged = false; // We will break out of the loop if no work was accomplished @@ -448,7 +435,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP LandThrottle.AddBytes(qpack.Length); qchanged = true; - if (LandOutgoingPacketQueue.Count == 0) + if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land)) Empty.Add(ThrottleOutPacketType.Land); } @@ -461,7 +448,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP WindThrottle.AddBytes(qpack.Length); qchanged = true; - if (WindOutgoingPacketQueue.Count == 0) + if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind)) Empty.Add(ThrottleOutPacketType.Wind); } @@ -474,7 +461,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP CloudThrottle.AddBytes(qpack.Length); qchanged = true; - if (CloudOutgoingPacketQueue.Count == 0) + if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud)) Empty.Add(ThrottleOutPacketType.Cloud); } @@ -496,7 +483,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP TaskThrottle.AddBytes(qpack.Length); qchanged = true; - if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0) + if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task)) Empty.Add(ThrottleOutPacketType.Task); } @@ -509,7 +496,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP TextureThrottle.AddBytes(qpack.Length); qchanged = true; - if (TextureOutgoingPacketQueue.Count == 0) + if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture)) Empty.Add(ThrottleOutPacketType.Texture); } @@ -522,16 +509,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP AssetThrottle.AddBytes(qpack.Length); qchanged = true; - if (AssetOutgoingPacketQueue.Count == 0) + if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset)) Empty.Add(ThrottleOutPacketType.Asset); } } // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); - foreach (ThrottleOutPacketType t in Empty) - { + e = new List(Empty); + Empty.Clear(); + } + + foreach (ThrottleOutPacketType t in e) + { + if (GetQueueCount(t) == 0) TriggerOnQueueEmpty(t); - } } } @@ -552,7 +543,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ProcessThrottle(); } - private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue q, LLQueItem item) + private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue q, LLQueItem item, ThrottleOutPacketType itemType) { // The idea.. is if the packet throttle queues are empty // and the client is under throttle for the type. Queue @@ -568,6 +559,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP throttle.AddBytes(item.Length); TotalThrottle.AddBytes(item.Length); SendQueue.Enqueue(item); + lock (this) + { + if (!Empty.Contains(itemType)) + Empty.Add(itemType); + } } catch (Exception e) { -- cgit v1.1 From 746dad7b74fb6e353cca3b5a09e0c3be88155784 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 1 Oct 2009 20:32:22 -0700 Subject: Locking the priority queue to see if it gets pass that exception. --- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 35 +++++++++++++++++----- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 70af8e8..97107ce 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -77,7 +77,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KImage imgrequest; // Do a linear search for this texture download - m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest); + lock (m_priorityQueue) + m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest); if (imgrequest != null) { @@ -85,7 +86,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP { //m_log.Debug("[TEX]: (CAN) ID=" + newRequest.RequestedAssetID); - try { m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle); } + try + { + lock (m_priorityQueue) + m_priorityQueue.Delete(imgrequest.m_priorityQueueHandle); + } catch (Exception) { } } else @@ -108,8 +113,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP //Update the requested priority imgrequest.m_requestedPriority = newRequest.Priority; - try { m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest); } - catch (Exception) { imgrequest.m_priorityQueueHandle = null; m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); } + try + { + lock (m_priorityQueue) + m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest); + } + catch (Exception) + { + imgrequest.m_priorityQueueHandle = null; + lock (m_priorityQueue) + m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); + } //Run an update imgrequest.RunUpdate(); @@ -152,7 +166,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP imgrequest.m_requestedPriority = newRequest.Priority; //Add this download to the priority queue - m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); + lock (m_priorityQueue) + m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); //Run an update imgrequest.RunUpdate(); @@ -212,7 +227,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP { while (m_priorityQueue.Count > 0) { - J2KImage imagereq = m_priorityQueue.FindMax(); + J2KImage imagereq = null; + lock (m_priorityQueue) + imagereq = m_priorityQueue.FindMax(); if (imagereq.m_decoded == true) { @@ -230,7 +247,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (imagereq.SendPackets(m_client, maxpack)) { // Send complete. Destroy any knowledge of this transfer - try { m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); } + try + { + lock (m_priorityQueue) + m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); + } catch (Exception) { } } } -- cgit v1.1 From 3ba36bb4d8ef44514da85702c86aa49808774c72 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 2 Oct 2009 05:49:27 +0100 Subject: Restore the missing image handling to the image manager. The missing image packet crashes Hippo without a message. --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 16 ++- .../Region/ClientStack/LindenUDP/LLClientView.cs | 3 +- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 144 +++++++++++---------- 3 files changed, 92 insertions(+), 71 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index b958b01..5f549b5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -235,7 +235,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0) { - m_log.Debug("No textures queued, sending one packet to kickstart it"); + //m_log.Debug("No textures queued, sending one packet to kickstart it"); SendPacket(m_imageManager.Client); } } @@ -333,7 +333,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (Data == null) { client.SendImageNotFound(m_requestedUUID); - m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is al", m_requestedUUID); + m_log.WarnFormat("[TEXTURE]: Got null Data element on a asset {0}.. and the missing image Data property is also null", m_requestedUUID); return true; } // Do we have less then 1 packet's worth of data? @@ -374,8 +374,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (asset == null || asset.Data == null) { - m_asset = null; - m_decoded = true; + if (m_imageManager.MissingImage != null) + { + m_asset = m_imageManager.MissingImage; + m_assetDataLength = m_asset.Data.Length; + } + else + { + m_asset = null; + m_decoded = true; + } } else { diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 23fa1a3..f05c490 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -141,7 +141,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_primTerseUpdateRate = 10; protected int m_primFullUpdateRate = 14; - protected int m_textureSendLimit = 100; + protected int m_textureSendLimit = 20; protected int m_textureDataLimit = 10; protected int m_packetMTU = 1400; @@ -3139,7 +3139,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP switch (queue) { case ThrottleOutPacketType.Texture: - m_log.Debug("Texture queue empty"); ProcessTextureRequests(); break; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index fcdf857..a484fdf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -52,6 +52,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private bool m_shuttingdown = false; private long m_lastloopprocessed = 0; + private AssetBase m_missingImage = null; private LLClientView m_client; //Client we're assigned to private IAssetService m_assetCache; //Asset Cache @@ -62,6 +63,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_client = client; m_assetCache = pAssetCache; + if (pAssetCache != null) + m_missingImage = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f"); + else + m_log.Error("[ClientView] - couldn't set missing image asset, falling back to missing image packet. This is known to crash the client"); + m_j2kDecodeModule = pJ2kDecodeModule; } @@ -70,6 +76,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP get { return m_client; } } + public AssetBase MissingImage + { + get { return m_missingImage; } + } + public void EnqueueReq(TextureRequestArgs newRequest) { //newRequest is the properties of our new texture fetch request. @@ -183,90 +194,93 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool ProcessImageQueue(int count, int maxpack) { - //count is the number of textures we want to process in one go. - //As part of this class re-write, that number will probably rise - //since we're processing in a more efficient manner. + lock (this) + { + //count is the number of textures we want to process in one go. + //As part of this class re-write, that number will probably rise + //since we're processing in a more efficient manner. - // this can happen during Close() - if (m_client == null) - return false; + // this can happen during Close() + if (m_client == null) + return false; - int numCollected = 0; + int numCollected = 0; - //Calculate our threshold - int threshold; - if (m_lastloopprocessed == 0) - { - if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) - return false; - //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below - threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; - m_lastloopprocessed = DateTime.Now.Ticks; - } - else - { - double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; - throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; + //Calculate our threshold + int threshold; + if (m_lastloopprocessed == 0) + { + if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) + return false; + //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below + threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300; + m_lastloopprocessed = DateTime.Now.Ticks; + } + else + { + double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond; + throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current; - //Average of 1000 bytes per packet - throttleseconds = throttleseconds / 1000; + //Average of 1000 bytes per packet + throttleseconds = throttleseconds / 1000; - //Safe-zone multiplier of 2.0 - threshold = (int)(throttleseconds * 2.0); - m_lastloopprocessed = DateTime.Now.Ticks; + //Safe-zone multiplier of 2.0 + threshold = (int)(throttleseconds * 2.0); + m_lastloopprocessed = DateTime.Now.Ticks; - } + } - if (m_client.PacketHandler == null) - return false; + if (m_client.PacketHandler == null) + return false; - if (m_client.PacketHandler.PacketQueue == null) - return false; + if (m_client.PacketHandler.PacketQueue == null) + return false; - if (threshold < 10) - threshold = 10; + if (threshold < 10) + threshold = 10; - //Uncomment this to see what the texture stack is doing - //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); - if (m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold) - { - while (m_priorityQueue.Count > 0) + //Uncomment this to see what the texture stack is doing + //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.getQueueCount(ThrottleOutPacketType.Texture).ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString()); + if (true) //m_client.PacketHandler.PacketQueue.GetQueueCount(ThrottleOutPacketType.Texture) < threshold) { - J2KImage imagereq = null; - lock (m_priorityQueue) - imagereq = m_priorityQueue.FindMax(); - - if (imagereq.m_decoded == true) + while (m_priorityQueue.Count > 0) { - // we need to test this here now that we are dropping assets - if (!imagereq.m_hasasset) - { - m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID); - imagereq.RunUpdate(); - continue; - } - - ++numCollected; + J2KImage imagereq = null; + lock (m_priorityQueue) + imagereq = m_priorityQueue.FindMax(); - //SendPackets will send up to ten packets per cycle - if (imagereq.SendPackets(m_client, maxpack)) + if (imagereq.m_decoded == true) { - // Send complete. Destroy any knowledge of this transfer - try - { - lock (m_priorityQueue) - m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); + // we need to test this here now that we are dropping assets + if (!imagereq.m_hasasset) + { + m_log.WarnFormat("[LLIMAGE MANAGER]: Re-requesting the image asset {0}", imagereq.m_requestedUUID); + imagereq.RunUpdate(); + continue; + } + + ++numCollected; + + //SendPackets will send up to ten packets per cycle + if (imagereq.SendPackets(m_client, maxpack)) + { + // Send complete. Destroy any knowledge of this transfer + try + { + lock (m_priorityQueue) + m_priorityQueue.Delete(imagereq.m_priorityQueueHandle); + } + catch (Exception) { } } - catch (Exception) { } } - } - if (numCollected == count) - break; + if (numCollected == count) + break; + } } - } - return m_priorityQueue.Count > 0; + return m_priorityQueue.Count > 0; + } } //Faux destructor -- cgit v1.1 From 9b342d3e0d85e64e143b9fe1010e4e27135e24ec Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 2 Oct 2009 12:00:42 -0700 Subject: * Changed the flush logic to drop packets in non-transactional streams, and to not fire any "put more data in the queues" callbacks * Minor tweaks to code formatting to make the callback chain for packet queuing easier to follow --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 14 ++---- .../ClientStack/LindenUDP/LLPacketHandler.cs | 6 +-- .../Region/ClientStack/LindenUDP/LLPacketQueue.cs | 58 +++++----------------- 3 files changed, 19 insertions(+), 59 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index f05c490..43d29fd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3131,26 +3131,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - // Unlike the other timers, this one is only started after - // the first request is seen. - void HandleQueueEmpty(ThrottleOutPacketType queue) { switch (queue) { - case ThrottleOutPacketType.Texture: - ProcessTextureRequests(); - break; + case ThrottleOutPacketType.Texture: + ProcessTextureRequests(); + break; } } void ProcessTextureRequests() { if (m_imageManager != null) - { - m_imageManager.ProcessImageQueue(m_textureSendLimit, - m_textureDataLimit); - } + m_imageManager.ProcessImageQueue(m_textureSendLimit, m_textureDataLimit); } void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs index 37f6ca7..e98a360 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs @@ -776,10 +776,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { QueueEmpty handlerQueueEmpty = OnQueueEmpty; - if (handlerQueueEmpty == null) - return; - - handlerQueueEmpty(queue); + if (handlerQueueEmpty != null) + handlerQueueEmpty(queue); } // Convert the packet to bytes and stuff it onto the send queue diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index 8484846..0f1acb1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -273,55 +273,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP { lock (this) { - while (PacketsWaiting()) + // These categories do not contain transactional packets so we can safely drop any pending data in them + LandOutgoingPacketQueue.Clear(); + WindOutgoingPacketQueue.Clear(); + CloudOutgoingPacketQueue.Clear(); + TextureOutgoingPacketQueue.Clear(); + AssetOutgoingPacketQueue.Clear(); + + // Now comes the fun part.. we dump all remaining resend and task packets into the send queue + while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) { - //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. if (ResendOutgoingPacketQueue.Count > 0) - { SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue()); - } - if (LandOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(LandOutgoingPacketQueue.Dequeue()); - TriggerOnQueueEmpty(ThrottleOutPacketType.Land); - } - if (WindOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(WindOutgoingPacketQueue.Dequeue()); - TriggerOnQueueEmpty(ThrottleOutPacketType.Wind); - } - if (CloudOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(CloudOutgoingPacketQueue.Dequeue()); - TriggerOnQueueEmpty(ThrottleOutPacketType.Cloud); - } - bool tasksSent = false; + if (TaskOutgoingPacketQueue.Count > 0) - { - tasksSent = true; SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); - } + if (TaskLowpriorityPacketQueue.Count > 0) - { - tasksSent = true; SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); - } - if (tasksSent) - { - TriggerOnQueueEmpty(ThrottleOutPacketType.Task); - } - if (TextureOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(TextureOutgoingPacketQueue.Dequeue()); - TriggerOnQueueEmpty(ThrottleOutPacketType.Texture); - } - if (AssetOutgoingPacketQueue.Count > 0) - { - SendQueue.Enqueue(AssetOutgoingPacketQueue.Dequeue()); - TriggerOnQueueEmpty(ThrottleOutPacketType.Asset); - } } - // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); } } @@ -530,10 +500,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { QueueEmpty handlerQueueEmpty = OnQueueEmpty; - if (handlerQueueEmpty == null) - return; - - handlerQueueEmpty(queue); + if (handlerQueueEmpty != null) + handlerQueueEmpty(queue); } private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) -- cgit v1.1