From: Alan Webb <alan_webb@us.ibm.com>

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.
0.6.6-post-fixes
Dr Scofield 2009-06-25 07:42:06 +00:00
parent 652bcf91d5
commit afd5f76648
4 changed files with 185 additions and 75 deletions

View File

@ -260,7 +260,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return false; return false;
} }
} }
public bool SendPackets(LLClientView client) public bool SendPackets(LLClientView client, int maxpack)
{ {
if (!m_completedSendAtCurrentDiscardLevel) if (!m_completedSendAtCurrentDiscardLevel)
@ -284,7 +284,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
int count = 0; int count = 0;
while (SendMore && count < 5 && m_packetNumber <= m_stopPacket) while (SendMore && count < maxpack && m_packetNumber <= m_stopPacket)
{ {
count++; count++;
SendMore = SendPacket(client); SendMore = SendPacket(client);
@ -391,6 +391,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
} }
else
{
m_packetNumber = m_stopPacket;
}
} }
} }
} }

View File

@ -80,6 +80,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates = private List<ObjectUpdatePacket.ObjectDataBlock> m_primFullUpdates =
new List<ObjectUpdatePacket.ObjectDataBlock>(); new List<ObjectUpdatePacket.ObjectDataBlock>();
private Timer m_textureRequestTimer;
private bool m_clientBlocked; private bool m_clientBlocked;
private int m_probesWithNoIngressPackets; private int m_probesWithNoIngressPackets;
@ -140,6 +142,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
protected int m_primTerseUpdateRate = 10; protected int m_primTerseUpdateRate = 10;
protected int m_primFullUpdateRate = 14; 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 int m_packetMTU = 1400;
protected IAssetService m_assetService; protected IAssetService m_assetService;
@ -344,6 +350,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private readonly IGroupsModule m_GroupsModule; private readonly IGroupsModule m_GroupsModule;
private AgentUpdateArgs lastarg = null;
//private TerrainUnacked handlerUnackedTerrain = null; //private TerrainUnacked handlerUnackedTerrain = null;
//** //**
@ -544,6 +552,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate", m_primFullUpdateRate = clientConfig.GetInt("FullUpdateRate",
m_primFullUpdateRate); 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); m_packetMTU = clientConfig.GetInt("PacketMTU", 1400);
} }
} }
@ -577,6 +594,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_avatarTerseUpdateTimer.Stop(); m_avatarTerseUpdateTimer.Stop();
m_primTerseUpdateTimer.Stop(); m_primTerseUpdateTimer.Stop();
m_primFullUpdateTimer.Stop(); m_primFullUpdateTimer.Stop();
m_textureRequestTimer.Stop();
// This is just to give the client a reasonable chance of // This is just to give the client a reasonable chance of
// flushing out all it's packets. There should probably // flushing out all it's packets. There should probably
@ -660,6 +678,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_avatarTerseUpdateTimer.Stop(); m_avatarTerseUpdateTimer.Stop();
m_primTerseUpdateTimer.Stop(); m_primTerseUpdateTimer.Stop();
m_primFullUpdateTimer.Stop(); m_primFullUpdateTimer.Stop();
m_textureRequestTimer.Stop();
} }
public void Restart() public void Restart()
@ -682,6 +701,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
m_primFullUpdateTimer.AutoReset = false; m_primFullUpdateTimer.AutoReset = false;
m_textureRequestTimer = new Timer(m_textureRequestRate);
m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
m_textureRequestTimer.AutoReset = false;
} }
public void Terminate() public void Terminate()
@ -914,6 +938,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_primFullUpdateTimer = new Timer(m_primFullUpdateRate); m_primFullUpdateTimer = new Timer(m_primFullUpdateRate);
m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates); m_primFullUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessPrimFullUpdates);
m_primFullUpdateTimer.AutoReset = false; m_primFullUpdateTimer.AutoReset = false;
m_textureRequestTimer = new Timer(m_textureRequestRate);
m_textureRequestTimer.Elapsed += new ElapsedEventHandler(ProcessTextureRequests);
m_textureRequestTimer.AutoReset = false;
m_scene.AddNewClient(this); m_scene.AddNewClient(this);
RefreshGroupMembership(); 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 # endregion
// Previously ClientView.API partial class // 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) void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e)
{ {
lock (m_primFullUpdates) lock (m_primFullUpdates)
@ -3237,7 +3301,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec) ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec)
{ {
ImageDataPacket im = new ImageDataPacket(); ImageDataPacket im = new ImageDataPacket();
im.Header.Reliable = true; im.Header.Reliable = false;
im.ImageID.Packets = numParts; im.ImageID.Packets = numParts;
im.ImageID.ID = ImageUUID; im.ImageID.ID = ImageUUID;
@ -3253,7 +3317,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData) public void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData)
{ {
ImagePacketPacket im = new ImagePacketPacket(); ImagePacketPacket im = new ImagePacketPacket();
im.Header.Reliable = true; im.Header.Reliable = false;
im.ImageID.Packet = partNumber; im.ImageID.Packet = partNumber;
im.ImageID.ID = imageUuid; im.ImageID.ID = imageUuid;
im.ImageData.Data = imageData; im.ImageData.Data = imageData;
@ -4727,14 +4791,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="Pack">OpenMetaverse.packet</param> /// <param name="Pack">OpenMetaverse.packet</param>
public void ProcessInPacket(Packet Pack) public void ProcessInPacket(Packet Pack)
{ {
// check if we've got a local packet handler for this packet.type. See RegisterLocalPacketHandlers()
if (ProcessPacketMethod(Pack)) 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; return;
} }
@ -5303,6 +5362,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
case PacketType.AgentUpdate: case PacketType.AgentUpdate:
if (OnAgentUpdate != null) if (OnAgentUpdate != null)
{ {
bool update = false;
AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack; AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack;
#region Packet Session and User Check #region Packet Session and User Check
@ -5315,26 +5375,58 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion #endregion
AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData; 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; // We can only check when we have something to check
if (handlerAgentUpdate != null) // against.
OnAgentUpdate(this, arg);
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; 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 // in the end, we null this, so we have to check if it's null
if (m_imageManager != null) if (m_imageManager != null)
{
m_imageManager.EnqueueReq(args); m_imageManager.EnqueueReq(args);
m_textureRequestTimer.Start();
}
} }
} }
break; break;
case PacketType.TransferRequest: case PacketType.TransferRequest:
//m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request"); //m_log.Debug("ClientView.ProcessPackets.cs:ProcessInPacket() - Got transfer request");
@ -9502,10 +9598,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#endregion #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); PacketPool.Instance.ReturnPacket(Pack);
} }
private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Imaging; using OpenMetaverse.Imaging;
@ -96,46 +97,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP
J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID]; 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.requestSequence > imgrequest.m_lastSequence)
{ {
imgrequest.m_lastSequence = newRequest.requestSequence; imgrequest.m_lastSequence = newRequest.requestSequence;
//First of all, is this being killed? //Check the priority
//if (newRequest.Priority == 0.0f && newRequest.DiscardLevel == -1)
//{
//Do nothing (leaving the if for future use)
//}
//else
//{
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 //Update the requested discard level
double priority = imgrequest.m_requestedPriority; imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
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 //Update the requested packet number
imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel; imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
//Update the requested packet number //Check if this will create an outstanding texture request
imgrequest.m_requestedPacketNumber = newRequest.PacketNumber; bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel;
//Run an update
//Check if this will create an outstanding texture request imgrequest.RunUpdate();
bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel;
//Run an update
imgrequest.RunUpdate();
if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded)
{
m_outstandingtextures++;
}
//} if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded)
{
Interlocked.Increment(ref m_outstandingtextures);
}
} }
} }
else 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() // 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. //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 //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_lastloopprocessed == 0)
{ {
if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null) 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 //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; threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
m_lastloopprocessed = DateTime.Now.Ticks; m_lastloopprocessed = DateTime.Now.Ticks;
@ -239,10 +238,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
if (m_client.PacketHandler == null) if (m_client.PacketHandler == null)
return; return false;
if (m_client.PacketHandler.PacketQueue == null) if (m_client.PacketHandler.PacketQueue == null)
return; return false;
//First of all make sure our packet queue isn't above our threshold //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; bool justreset = false;
for (int x = m_priorities.Count - 1; x > -1; x--) for (int x = m_priorities.Count - 1; x > -1; x--)
{ {
J2KImage imagereq = m_imagestore[m_priorities.Values[x]]; J2KImage imagereq = m_imagestore[m_priorities.Values[x]];
if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel) if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel)
{ {
numCollected++; numCollected++;
//SendPackets will send up to ten packets per cycle //SendPackets will send up to ten packets per cycle
if (imagereq.SendPackets(m_client)) if (imagereq.SendPackets(m_client, maxpack))
{ {
//Send complete //Send complete
if (!imagereq.m_completedSendAtCurrentDiscardLevel) if (!imagereq.m_completedSendAtCurrentDiscardLevel)
{ {
imagereq.m_completedSendAtCurrentDiscardLevel = true; imagereq.m_completedSendAtCurrentDiscardLevel = true;
m_outstandingtextures--; Interlocked.Decrement(ref m_outstandingtextures);
//Re-assign priority to bottom //Re-assign priority to bottom
//Remove the old priority //Remove the old priority
m_priorities.Remove(imagereq.m_designatedPriorityKey); 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 justreset = true; //prevents us from getting stuck in a loop
} }
} }
} }
return m_outstandingtextures != 0;
} }
//Faux destructor //Faux destructor

View File

@ -1208,12 +1208,15 @@
[LLClient] [LLClient]
; Resend packets markes as reliable until they are received ; Resend packets markes as reliable until they are received
;
;ReliableIsImportant = false ;ReliableIsImportant = false
; Maximum number of times to resend packets marked reliable ; Maximum number of times to resend packets marked reliable
;
;MaxReliableResends = 3 ;MaxReliableResends = 3
; Configures how ObjectUpdates are compressed. ; Configures how ObjectUpdates are compressed.
;
;TerseUpdatesPerPacket=10 ;TerseUpdatesPerPacket=10
;FullUpdatesPerPacket=14 ;FullUpdatesPerPacket=14
;TerseUpdateRate=10 ;TerseUpdateRate=10
@ -1221,6 +1224,22 @@
;PacketMTU = 1400 ;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]. ;; These are defatuls that are overwritten below in [Architecture].