From 6e0c79b8fe76c7d2c983cb7b258a75a3300e78f2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 1 Oct 2009 17:42:13 -0700 Subject: [PATCH] * 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) --- .../Region/ClientStack/LindenUDP/J2KImage.cs | 499 +++++++++--------- .../ClientStack/LindenUDP/LLImageManager.cs | 330 +++++------- .../Agent/TextureSender/J2KDecoderModule.cs | 1 - .../TexturesAssetSet/TexturesAssetSet.xml | 7 + .../TexturesAssetSet/default_avatar.jp2 | Bin 0 -> 36044 bytes 5 files changed, 363 insertions(+), 474 deletions(-) create mode 100644 bin/assets/TexturesAssetSet/default_avatar.jp2 diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index d86b1237bc..1448722fce 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 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; - private AssetBase m_asset = null; - private int m_assetDataLength = 0; - - private LLImageManager m_image; - - 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,204 +108,45 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void DropAsset() + #endregion Properties + + public J2KImage(LLImageManager imageManager) { - //m_log.WarnFormat("[LLIMAGE MANAGER]: Dropping texture asset {0}", m_requestedUUID); - m_asset = null; - m_hasasset = false; - m_asset_requested = false; + m_imageManager = imageManager; } - public void J2KDecodedCallback(UUID AssetId, OpenJPEG.J2KLayerInfo[] layers) - { - m_image.m_outstandingtextures++; - Layers = layers; - m_decoded = true; - RunUpdate(); - } - - public void AssetDataCallback(UUID AssetID, AssetBase asset) - { - m_hasasset = true; - if (asset == null || asset.Data == null) - { - m_asset = m_MissingSubstitute; - } - else - { - 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); - - } - - private int GetPacketForBytePosition(int bytePosition) - { - return ((bytePosition - cFirstPacketSize + cImagePacketSize - 1) / cImagePacketSize) + 1; - } - - 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; - } - 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; - } - - public 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 <= cFirstPacketSize) - { - // 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[cFirstPacketSize]; - try - { - Buffer.BlockCopy(m_asset.Data, 0, firstImageData, 0, (int)cFirstPacketSize); - 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 bool SendPacket(LLClientView client) - { - bool complete = false; - int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : cImagePacketSize; - - try - { - if ((CurrentBytePosition() + cImagePacketSize) > m_assetDataLength) - { - imagePacketSize = LastPacketSize(); - complete=true; - if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength) - { - imagePacketSize = m_assetDataLength - CurrentBytePosition(); - complete = true; - } - } - - // It's concievable that the client might request packet one - // from a one packet image, which is really packet 0, - // which would leave us with a negative imagePacketSize.. - if (imagePacketSize > 0) - { - byte[] imageData = new byte[imagePacketSize]; - try - { - Buffer.BlockCopy(m_asset.Data, CurrentBytePosition(), imageData, 0, imagePacketSize); - } - catch (Exception e) - { - m_log.Error("Error copying texture block. Out of memory? imagePacketSize was " + imagePacketSize.ToString() + " on packet " + m_packetNumber.ToString() + " out of " + m_stopPacket.ToString() + ". Exception: " + e.ToString()); - return false; - } - - //Send the packet - client.SendImageNextPart((ushort)(m_packetNumber-1), m_requestedUUID, imageData); - } - if (complete) - { - return false; - } - else - { - return true; - } - } - catch (Exception) - { - return false; - } - } public bool SendPackets(LLClientView client, int maxpack) { - - if (!m_completedSendAtCurrentDiscardLevel) + if (m_packetNumber <= m_stopPacket) { - if (m_packetNumber <= m_stopPacket) + bool SendMore = true; + if (!m_sentinfo || (m_packetNumber == 0)) { - bool SendMore = true; - if (!m_sentinfo || (m_packetNumber == 0)) + if (SendFirstPacket(client)) { - 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; + 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; } + return false; } @@ -323,19 +158,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!m_hasasset) { - if (!m_asset_requested) { m_asset_requested = true; m_assetCache.Get(m_requestedUUID.ToString(), this, AssetReceived); - } - } else { - - if (!m_decoded) { //We need to decode the requested image first @@ -362,59 +192,202 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KDecodedCallback(m_requestedUUID, new OpenJPEG.J2KLayerInfo[0]); } } - } else { - //discardLevel of -1 means just update the priority - if (m_requestedDiscardLevel != -1) + // Check for missing image asset data + if (m_asset == null || m_asset.Data == null) { - //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; - } + // FIXME: + m_packetNumber = m_stopPacket; + return; + } - //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 + 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(); } - //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; - } - - if (m_packetNumber <= m_stopPacket) - { - m_completedSendAtCurrentDiscardLevel = false; - } } - } - else - { - m_packetNumber = m_stopPacket; + else + { + m_stopPacket = TexturePacketCount(); + } + + m_packetNumber = m_requestedPacketNumber; } } } } + + private bool SendPacket(LLClientView client) + { + bool complete = false; + int imagePacketSize = ((int)m_packetNumber == (TexturePacketCount())) ? LastPacketSize() : IMAGE_PACKET_SIZE; + + try + { + if ((CurrentBytePosition() + IMAGE_PACKET_SIZE) > m_assetDataLength) + { + imagePacketSize = LastPacketSize(); + complete = true; + if ((CurrentBytePosition() + imagePacketSize) > m_assetDataLength) + { + imagePacketSize = m_assetDataLength - CurrentBytePosition(); + complete = true; + } + } + + // It's concievable that the client might request packet one + // from a one packet image, which is really packet 0, + // which would leave us with a negative imagePacketSize.. + if (imagePacketSize > 0) + { + byte[] imageData = new byte[imagePacketSize]; + try + { + Buffer.BlockCopy(m_asset.Data, CurrentBytePosition(), imageData, 0, imagePacketSize); + } + catch (Exception e) + { + m_log.Error("Error copying texture block. Out of memory? imagePacketSize was " + imagePacketSize.ToString() + " on packet " + m_packetNumber.ToString() + " out of " + m_stopPacket.ToString() + ". Exception: " + e.ToString()); + return false; + } + + //Send the packet + client.SendImageNextPart((ushort)(m_packetNumber - 1), m_requestedUUID, imageData); + } + if (complete) + { + return false; + } + else + { + return true; + } + } + catch (Exception) + { + return false; + } + } + + private int GetPacketForBytePosition(int bytePosition) + { + return ((bytePosition - FIRST_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1; + } + + 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) + { + lastsize = IMAGE_PACKET_SIZE; + } + return lastsize; + } + + private int CurrentBytePosition() + { + if (m_packetNumber == 0) + return 0; + if (m_packetNumber == 1) + return FIRST_PACKET_SIZE; + + int result = FIRST_PACKET_SIZE + ((int)m_packetNumber - 2) * IMAGE_PACKET_SIZE; + if (result < 0) + { + result = FIRST_PACKET_SIZE; + } + return result; + } + + 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 + { + 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(); + } + + private void AssetReceived(string id, Object sender, AssetBase asset) + { + UUID assetID = UUID.Zero; + if (asset != null) + assetID = asset.FullID; + + AssetDataCallback(assetID, asset); + + } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index b039049ad6..a82eaaecb1 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)) + // Do a linear search for this texture download + m_priorityQueue.Find(delegate(J2KImage img) { return img.m_requestedUUID == newRequest.RequestedAssetID; }, out imgrequest); + + if (imgrequest != null) { - //Check the packet sequence to make sure this isn't older than - //one we've already received - - J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID]; - - // This is the inbound request sequence number. We can ignore - // "old" ones. - - if (newRequest.requestSequence > imgrequest.m_lastSequence) + if (newRequest.DiscardLevel == -1 && newRequest.Priority == 0f) { + m_log.Debug("[JPEG2000]: (CAN) ID=" + newRequest.RequestedAssetID); - imgrequest.m_lastSequence = newRequest.requestSequence; + 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); - //Check the 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 + //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 packet number + imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; + + //Update the requested priority imgrequest.m_requestedPriority = newRequest.Priority; - imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); - } + try { m_priorityQueue.Replace(imgrequest.m_priorityQueueHandle, imgrequest); } + catch (Exception) { imgrequest.m_priorityQueueHandle = null; m_priorityQueue.Add(ref imgrequest.m_priorityQueueHandle, imgrequest); } - //Update the requested discard level - imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; - - //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(); - - if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) - { - Interlocked.Increment(ref m_outstandingtextures); + //Run an update + imgrequest.RunUpdate(); } } } else { - J2KImage imgrequest = new J2KImage(this); + 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 missing substitute - imgrequest.m_MissingSubstitute = m_missingsubstitute; + imgrequest = new J2KImage(this); - //Assign our decoder module - imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; + //Assign our decoder module + imgrequest.m_j2kDecodeModule = m_j2kDecodeModule; - //Assign our asset cache module - imgrequest.m_assetCache = m_assetCache; + //Assign our asset cache module + imgrequest.m_assetCache = m_assetCache; - //Assign a priority based on our request - imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority); + //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; + //Assign the requested priority + imgrequest.m_requestedPriority = newRequest.Priority; - m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest); - - //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) - { - - //First, find out if we can just assign directly - if (m_priorityresolver.ContainsKey((int)pPriority) == false) - { - m_priorities.Add((double)((int)pPriority), pAssetID); - m_priorityresolver.Add((int)pPriority, 0); - return (double)((int)pPriority); - } - 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; - } - - - - } - 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. // this can happen during Close() if (m_client == 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; - } + //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; - if (m_client.PacketHandler == null) - return false; + //Average of 1000 bytes per packet + throttleseconds = throttleseconds / 1000; - if (m_client.PacketHandler.PacketQueue == null) - return false; + //Safe-zone multiplier of 2.0 + threshold = (int)(throttleseconds * 2.0); + m_lastloopprocessed = DateTime.Now.Ticks; - //First of all make sure our packet queue isn't above our threshold + } + + if (m_client.PacketHandler == null) + return false; + + if (m_client.PacketHandler.PacketQueue == null) + return false; + + 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) + + if (numCollected == count) 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 - } } } - 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 49f7f48508..a0f359b629 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; diff --git a/bin/assets/TexturesAssetSet/TexturesAssetSet.xml b/bin/assets/TexturesAssetSet/TexturesAssetSet.xml index 0352c99e60..773de446bd 100644 --- a/bin/assets/TexturesAssetSet/TexturesAssetSet.xml +++ b/bin/assets/TexturesAssetSet/TexturesAssetSet.xml @@ -401,4 +401,11 @@ + +
+ + + + +
diff --git a/bin/assets/TexturesAssetSet/default_avatar.jp2 b/bin/assets/TexturesAssetSet/default_avatar.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..116b860d80da5b981aadc1bc3aab327a6ab16c28 GIT binary patch literal 36044 zcmV)MK)ApEPybN>F8}}l00IC200IC2000000000000IC200IC200000000000|x;C z2LS;G0RjI~01N;C00jXB1Oxy8|6BkgB6l8l0Cxa(;%@+N0B+)EOlM4IWKaW815ia* z(pS<~VgF6BnUC+xm zme2c{R*ad{hD-UJQ38JEph~lsj`sF2G?R>ImPzxox&Vq^WR^6-FXwV#3ZIKry}penpxej9;AGFA8&L~_NIA|Vr)$Cq z9?e0J{EV2g3xoFu&NS|EZ_PO^{E3~a!R%GsVzsKvzOw%w!}1VQc*ny@9}N^H6}CVf zB(&uD7+GOr8sHJkDH&Kn-3`^Qpdc^M#8X#&-%Q*#)+s;NI!42yMA$>t|RyaWLuoQrR#c;s7pHs0b z8QRX^j7OZUfPjF2fPjF2^~lpTH>DKUH&!wc<(&+tY2g)&quhOvA*an2heVWh%vK8vW;K2O!2iVWCV zFa%5JcFI-8bJCDwMMf6*`$= z6LoP%>bX!)6$uS%=lOAEi>e4%PcX4)?-ougU~{0 zi5GmmO(mL*Shp8I`lQTLPO|JFczQYJUV%^?^$dupe2x0y5#<*HViegB`xKGX! z%G+gZvZ0Wx&Khd|eyj6PoPGQh&x2xz!=!-wPxLf{c|bjWXz9x4CNdt}*Q>R zMMrmKOYHa$LG?rn9`zjB)&CRpeVK_OykWUFS}eoT-S2q;kOsGckg z6?LU$Qq24Y@e=ULE)$FyBcCgns+iWo3Mp1Q2<=lLH-Iz7gBWsd^RHJO0Mxw5JG*kw zg+#|+C}06#gl2WFA}pY0a&d8Mg(`W0TxL|GA2w37j)SjC1`fK=9{SufD{kAh zvBckH&=&I!{VtKdrV_)-!{BU|%-DVNT>K;+P;AR(-_C5`gy}Up!WITD#oAB8Rr@gh z-Y%`tTj~%@OZc}YrFRFz;7SPKh}8G44KesQZRIK`H=uG$D;2%NNgCAo{1(XgdMX{# z!23(in2b8$hh{n?zG}%%bDFRK+zOgmEf@n_MD--Zz^yEii8d9*OS=~Sg2EV43EiGD(RB=XZheOq1+CMqvY!u z_AiTtfKG6UXeu^U@9=}11Eny0`8c6$ni(j4^BUQ=)?K7J!}1NpSM;1ziGN2>l)!aN-lt&M=(u9#^M$6ua3!A~Tdn=u0cPTjn_+#QTV1IWN z;wbsNDJ4r9*YvTmE^GB*jZ>B>kI(v2;l|T%^9FlL7dkOb5VLJ6iBT)SMU`Dd^(e`P zd&sysVyw5j@q5AiqY{pQCeFW|*&gD3yc+lMN8bX-SJDJ9`&tt6=YW94r&6+P7jT+u zULWAdtR6>K&A5QWsetPrRczfy04U8n+e^&CH*?%I$mh5Iv0E(m`^`JV0JmoaBUyo? zUgaONpuvH9Hxozn9;M(+%g|dIjKx3-nX89Z`k;u#pS@*aurbgT#Z9NYQ@U$Uw#0B- z^F25S^xA{Y`-e`rf0R|&(ixOw=NME-dYVLKAj z zrx$T@$yWHWRRRd}A=^@yd0rqg$hP~JOYeZ>&v`NFb=+it4ekUbw#Jfih^FQ!2vxxk zF^6V#TSkC@(tIcY>5Mje*?@rjY#_lTdc6Hlh63#%pwGSJrdc|Uh_piZm7sq&!;HI~ z9W5lStgYNGe5^Z5enbs%f+j^NFhzGJ@$H0yi4uFB>c3LA<~s^}$>Da%E{F1k4iZ-%0Dk0F8 z{TrxM^u*`Gc&r4dS_dorEOhBK8;ZeGYp%<=d#X!IG?*z_gW~Cuq}coAk4hC136Rgy zb(v*8G%R0y{A>UM(F6*gCSq7)H7@SCMqEye;WV9~%|T#F9NJokrHpAhpf*c^*_5rv zpq)7{xJtGQmAkN~+i}k6v{0wtq?1prFpoMPw{V4(RO@;a*ZHg869FQO@NDX zDTvq zC$DYTlf254pMX3*uyIP!>Dv^UOXA!nIPt%(y$muwILl6iTRieIib=9C(p2mgZXtpt zQakBOYr9GG&#+@5~Of4NhN(}r+zY-tD*dVJfVI>SP$kjO zp&-9ZSE4>Y#LYX2Buxx0R=@)uUM5Y(NF-<@?@C% zHC`xZxUP*M5ZcIar+;ooN9L+xmr80Ci}=RJ?>SuWwXGO4$ufA(7>}^)Y(ne;#+}`; zrEDq3dlAw8pd1w&6ax0^`ApyF99w|=NdF3n@jJ#IkWIcTsL=C0Z*$}%JnrV{&XUS= z{PwnGNs%JT`4UgNh*o0!$25z@8!Jts!I&INF);I3%+N)&l>UUdU8Gev&^DK}6{qYY z_F$2qar&O>x|tZe&uq|wiVId#+F<)6%yFak>s*Q0<=L3CsZw=n*fI-{dsTSM7{+?2 z8%5S-#k$DVMlb;#Y*4d?)_Cqpv9RQTJo{4*@#;pFdvOE;Ib||B zHXBn?@vJgo2iA%&_9w-mdYA%Lsz`ARX73m^RtZbuN;lL(ZWT;hYx}`TA-t&x&3Mkq zvLX;q8v0=z=$;o-)#tws@{>4smZ^vzddEXfiA#(f;12g{>}@me*J&v(@5C zj$PIN9WdNM;H5@S?j!ZJEg*O()A+>wgZIPfR4G7q`x6Rl+%`!@w8z#iW8tN<5xRUy?FM^7;Q`v=ksvSpjX1BW8h< zpahr);hTX^(Jp21yfQ62)yFvIug$_NV%T^C8h2nn{@hMBz-V|kn(LZddfMIA@7ZUL zp8s@>Tt7ng2HwnVTz=24rpg}GM3o|vfz&MKi3HF!m1uh$X9qDJEm+*;ay5MUxo_-R z5@;@4z}Hno7mFnC0_MYOg_eQZgUp7&vN0bUqAc1=F8O$y#f~Ee3LEoTdcK?e4p?-m zxmYhZoi&d{9L=%9F#SUrNjHmj1Jz-WKZ_W2FS4&6{QNxUFXXrU&eA4cJYU1CS!VRk z2yg@oDiB_G`D}c`E%jJZ{CW@KyTJIXpynS>UiN@1EP`{mh-kRm_^DqMG@>D1)dxlS$HKGis*ewT86Z!d~B-vvD0J@FeSOMR4 z@)Bpg{8|s?(BCC2)o>zcZqFU5KHwgy**)MG>qnWdj6v7rKQ*{8a6h<}XoC~wsV~`9 znJ0gV(=P*df7mlj!elYoK!%>1n&RC0DF0f@asc4e3}sI1o+) zY|;^XWe35E{-k7;KOh8scR(EUGn{Ayn<^%JzTJAd7+&FJY{1Mh>tsYEPoUU>Fz zO1RKA;Kx89{{VAxVgF%8rr?>*C&u+7e#BS{Z9QZ1_tDrALJ*H{Sf>fA_A!Pw@@R_HK{m4YD?x5`RaxX1?QU0D6slaxxs-4RvG>mGT7goU7!O8&vtRz zo<<7HUcTfcT&?3BBD;ipm4!_&Hye(*#93PeHoQ|O2e%uexQ%CCCzes%k^4>xPpdeQ zDp;ha{N&&J$&?~U>fXqsS6Vd+AJUd>@e>dTk#7hg+^XAdC&GLx{B?ukO1?K}x0BD} z^D`G5p|JO)M4%7Msqv>sGx`vv8_~cZsx-jZr@aaBpTK2#!p(VKOEBLr6e8hr1-fVKAblHsSo^a}H^vzQ`J zlXRfczwRVFnd=1fJggv6t(i5+1Cbixm6vp?adJ`Domu3k7=(}Ucn}?uM+K$eX8`

Df$Q&0`HBIJHYoFmU%^>bFfzxR;u1r4* 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 literal 0 HcmV?d00001