From afd5f76648740b80fdfe6cfdfca82e3def5baf03 Mon Sep 17 00:00:00 2001 From: Dr Scofield Date: Thu, 25 Jun 2009 07:42:06 +0000 Subject: From: Alan Webb This change moves texture send processing out of the main packet processing loop and moves it to a timer based processing cycle. Texture packets are sent to the client consistently over time. The timer is discontinued whenever there are no textures to transmit. The behavior of the texture sending mechanism is controlled by three variables in the LLCLient section of the config file: [1] TextureRequestRate (mS) determines how many times per second texture send processing will occur. The default is 100mS. [2] 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 and this is the default [3] TextureDataLimit determines how many packets will be sent for each of the selected textures. The old mechanism specified a value of 5, so this is the default. So the net effect is that TextureSendLimit*TextureDataLimit packets will be sent every TextureRequestRate mS. Once we have gotten a reasonable feeling for how these parameters affect overall processing, it would be nice to autonmically manage these values using information about the current status of the region and network. Note that this also resolves the pathologcal problem that previously existed which was that a seated avatar generated very few in-bound packets (theoretically) and would therefore be the least able to retrieve the images being displayed by a projector script. --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 8 +- .../Region/ClientStack/LindenUDP/LLClientView.cs | 152 +++++++++++++++++---- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 81 +++++------ 3 files changed, 166 insertions(+), 75 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5ba5df1..6cffd70 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 cda4d49..2884268 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 587579d..295a5e6 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 -- cgit v1.1