diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5ba5df1303..6cffd706dd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -260,7 +260,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return false; } } - public bool SendPackets(LLClientView client) + public bool SendPackets(LLClientView client, int maxpack) { if (!m_completedSendAtCurrentDiscardLevel) @@ -284,7 +284,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } int count = 0; - while (SendMore && count < 5 && m_packetNumber <= m_stopPacket) + while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket) { count++; SendMore = SendPacket(client); @@ -391,6 +391,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } } + else + { + m_packetNumber = m_stopPacket; + } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index cda4d49d5a..288426838d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -80,6 +80,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private List m_primFullUpdates = new List(); + private Timer m_textureRequestTimer; + private bool m_clientBlocked; private int m_probesWithNoIngressPackets; @@ -140,6 +142,10 @@ 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_packetMTU = 1400; protected IAssetService m_assetService; @@ -344,6 +350,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly IGroupsModule m_GroupsModule; + private AgentUpdateArgs lastarg = null; + //private TerrainUnacked handlerUnackedTerrain = null; //** @@ -544,6 +552,15 @@ 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); + + m_textureDataLimit = clientConfig.GetInt("TextureDataLimit", + m_textureDataLimit); + m_packetMTU = clientConfig.GetInt("PacketMTU", 1400); } } @@ -577,6 +594,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_avatarTerseUpdateTimer.Stop(); m_primTerseUpdateTimer.Stop(); m_primFullUpdateTimer.Stop(); + m_textureRequestTimer.Stop(); // This is just to give the client a reasonable chance of // flushing out all it's packets. There should probably @@ -660,6 +678,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_avatarTerseUpdateTimer.Stop(); m_primTerseUpdateTimer.Stop(); m_primFullUpdateTimer.Stop(); + m_textureRequestTimer.Stop(); } public void Restart() @@ -682,6 +701,11 @@ 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; + } public void Terminate() @@ -914,6 +938,11 @@ 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; + m_scene.AddNewClient(this); RefreshGroupMembership(); @@ -985,6 +1014,26 @@ 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 @@ -3032,6 +3081,21 @@ 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) + { + if (m_imageManager != null) + { + if (m_imageManager.ProcessImageQueue(m_textureSendLimit, + m_textureDataLimit)) + { + m_textureRequestTimer.Start(); + } + } + } + void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) { lock (m_primFullUpdates) @@ -3237,7 +3301,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec) { ImageDataPacket im = new ImageDataPacket(); - im.Header.Reliable = true; + im.Header.Reliable = false; im.ImageID.Packets = numParts; im.ImageID.ID = ImageUUID; @@ -3253,7 +3317,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData) { ImagePacketPacket im = new ImagePacketPacket(); - im.Header.Reliable = true; + im.Header.Reliable = false; im.ImageID.Packet = partNumber; im.ImageID.ID = imageUuid; im.ImageData.Data = imageData; @@ -4727,14 +4791,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// OpenMetaverse.packet public void ProcessInPacket(Packet Pack) { - // check if we've got a local packet handler for this packet.type. See RegisterLocalPacketHandlers() + if (ProcessPacketMethod(Pack)) { - //there is a handler registered that handled this packet type - - // in the end, we dereference this, so we have to check if it's null - if (m_imageManager != null) - m_imageManager.ProcessImageQueue(5); return; } @@ -5303,6 +5362,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP case PacketType.AgentUpdate: if (OnAgentUpdate != null) { + bool update = false; AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack; #region Packet Session and User Check @@ -5315,26 +5375,58 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData; - AgentUpdateArgs arg = new AgentUpdateArgs(); - arg.AgentID = x.AgentID; - arg.BodyRotation = x.BodyRotation; - arg.CameraAtAxis = x.CameraAtAxis; - arg.CameraCenter = x.CameraCenter; - arg.CameraLeftAxis = x.CameraLeftAxis; - arg.CameraUpAxis = x.CameraUpAxis; - arg.ControlFlags = x.ControlFlags; - arg.Far = x.Far; - arg.Flags = x.Flags; - arg.HeadRotation = x.HeadRotation; - arg.SessionID = x.SessionID; - arg.State = x.State; - handlerAgentUpdate = OnAgentUpdate; - if (handlerAgentUpdate != null) - OnAgentUpdate(this, arg); + // We can only check when we have something to check + // against. + + if (lastarg != null) + { + update = + ( + (x.BodyRotation != lastarg.BodyRotation) || + (x.CameraAtAxis != lastarg.CameraAtAxis) || + (x.CameraCenter != lastarg.CameraCenter) || + (x.CameraLeftAxis != lastarg.CameraLeftAxis) || + (x.CameraUpAxis != lastarg.CameraUpAxis) || + (x.ControlFlags != lastarg.ControlFlags) || + (x.Far != lastarg.Far) || + (x.Flags != lastarg.Flags) || + (x.State != lastarg.State) || + (x.HeadRotation != lastarg.HeadRotation) || + (x.SessionID != lastarg.SessionID) || + (x.AgentID != lastarg.AgentID) + ); + } + else + update = true; + + // These should be ordered from most-likely to + // least likely to change. I've made an initial + // guess at that. + + if (update) + { + AgentUpdateArgs arg = new AgentUpdateArgs(); + arg.AgentID = x.AgentID; + arg.BodyRotation = x.BodyRotation; + arg.CameraAtAxis = x.CameraAtAxis; + arg.CameraCenter = x.CameraCenter; + arg.CameraLeftAxis = x.CameraLeftAxis; + arg.CameraUpAxis = x.CameraUpAxis; + arg.ControlFlags = x.ControlFlags; + arg.Far = x.Far; + arg.Flags = x.Flags; + arg.HeadRotation = x.HeadRotation; + arg.SessionID = x.SessionID; + arg.State = x.State; + handlerAgentUpdate = OnAgentUpdate; + lastarg = arg; // save this set of arguments for nexttime + if (handlerAgentUpdate != null) + OnAgentUpdate(this, arg); + + handlerAgentUpdate = null; + } - handlerAgentUpdate = null; - //agenUpdate.AgentData.ControlFlags, agenUpdate.AgentData.BodyRotationa); } break; @@ -6392,10 +6484,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP // in the end, we null this, so we have to check if it's null if (m_imageManager != null) + { m_imageManager.EnqueueReq(args); + m_textureRequestTimer.Start(); + } } } break; + case PacketType.TransferRequest: //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); @@ -9502,10 +9598,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion } - // in the end, we dereference this, so we have to check if it's null - if (m_imageManager != null) - m_imageManager.ProcessImageQueue(10); PacketPool.Instance.ReturnPacket(Pack); + } private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 587579df8a..295a5e6f99 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -26,6 +26,7 @@ */ using System; +using System.Threading; using System.Collections.Generic; using OpenMetaverse; using OpenMetaverse.Imaging; @@ -96,46 +97,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID]; + // This is the inbound request sequence number. We can ignore + // "old" ones. + if (newRequest.requestSequence > imgrequest.m_lastSequence) { + imgrequest.m_lastSequence = newRequest.requestSequence; - //First of all, is this being killed? - //if (newRequest.Priority == 0.0f && newRequest.DiscardLevel == -1) - //{ - //Do nothing (leaving the if for future use) - //} - //else - //{ + //Check the priority + double priority = imgrequest.m_requestedPriority; + if (priority != newRequest.Priority) + { + //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); + } - //Check the priority - double priority = imgrequest.m_requestedPriority; - if (priority != newRequest.Priority) - { - //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 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 - //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) - { - m_outstandingtextures++; - } + imgrequest.RunUpdate(); - //} + if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded) + { + Interlocked.Increment(ref m_outstandingtextures); + } } } else @@ -198,10 +195,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - public void ProcessImageQueue(int count) + public bool ProcessImageQueue(int count, int maxpack) { + // this can happen during Close() - if (m_client == null) return; + 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 @@ -214,7 +213,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lastloopprocessed == 0) { if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) - return; + 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; @@ -239,10 +238,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP } if (m_client.PacketHandler == null) - return; + return false; if (m_client.PacketHandler.PacketQueue == null) - return; + return false; //First of all make sure our packet queue isn't above our threshold @@ -252,24 +251,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP { bool justreset = false; - - for (int x = m_priorities.Count - 1; x > -1; x--) { J2KImage imagereq = m_imagestore[m_priorities.Values[x]]; if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel) { - numCollected++; //SendPackets will send up to ten packets per cycle - if (imagereq.SendPackets(m_client)) + if (imagereq.SendPackets(m_client, maxpack)) { //Send complete if (!imagereq.m_completedSendAtCurrentDiscardLevel) { imagereq.m_completedSendAtCurrentDiscardLevel = true; - m_outstandingtextures--; + Interlocked.Decrement(ref m_outstandingtextures); //Re-assign priority to bottom //Remove the old priority m_priorities.Remove(imagereq.m_designatedPriorityKey); @@ -310,13 +306,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP justreset = true; //prevents us from getting stuck in a loop } - - } } - - + return m_outstandingtextures != 0; } //Faux destructor diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 91f86c5190..5f2b46ad8e 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1208,12 +1208,15 @@ [LLClient] ; Resend packets markes as reliable until they are received + ; ;ReliableIsImportant = false ; Maximum number of times to resend packets marked reliable + ; ;MaxReliableResends = 3 ; Configures how ObjectUpdates are compressed. + ; ;TerseUpdatesPerPacket=10 ;FullUpdatesPerPacket=14 ;TerseUpdateRate=10 @@ -1221,6 +1224,22 @@ ;PacketMTU = 1400 + ; TextureUpdateRate (mS) determines how many times per second + ; texture send processing will occur. The default is 100mS. + ; + ;TextureRequestRate = 100 + + ; TextureSendLimit determines how many different textures + ; will be considered on each cycle. Textures are selected + ; by priority. The old mechanism specified a value of 10 for + ; this parameter. + ; + ;TextureSendLimit = 10 + + ; TextureDataLimit determines how many packets will be sent for + ; each of the selected textures. Default is 5. + ; + ;TextureDataLimit = 5 ;; ;; These are defatuls that are overwritten below in [Architecture].