LLImageManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using OpenMetaverse;
  31. using OpenMetaverse.Imaging;
  32. using OpenSim.Framework;
  33. using OpenSim.Region.Framework.Interfaces;
  34. using OpenSim.Services.Interfaces;
  35. using log4net;
  36. using System.Reflection;
  37. namespace OpenSim.Region.ClientStack.LindenUDP
  38. {
  39. public class LLImageManager
  40. {
  41. //Public interfaces:
  42. //Constructor - (LLClientView, IAssetCache, IJ2KDecoder);
  43. //void EnqueueReq - (TextureRequestArgs)
  44. //ProcessImageQueue
  45. //Close
  46. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  47. private bool m_shuttingdown = false;
  48. private long m_lastloopprocessed = 0;
  49. private LLClientView m_client; //Client we're assigned to
  50. private IAssetService m_assetCache; //Asset Cache
  51. private IJ2KDecoder m_j2kDecodeModule; //Our J2K module
  52. private readonly AssetBase m_missingsubstitute; //Sustitute for bad decodes
  53. private Dictionary<UUID,J2KImage> m_imagestore; // Our main image storage dictionary
  54. private SortedList<double,UUID> m_priorities; // For fast image lookup based on priority
  55. private Dictionary<int, int> m_priorityresolver; //Enabling super fast assignment of images with the same priorities
  56. private const double doubleMinimum = .0000001;
  57. public int m_outstandingtextures = 0;
  58. //Constructor
  59. public LLImageManager(LLClientView client, IAssetService pAssetCache, IJ2KDecoder pJ2kDecodeModule)
  60. {
  61. m_imagestore = new Dictionary<UUID,J2KImage>();
  62. m_priorities = new SortedList<double,UUID>();
  63. m_priorityresolver = new Dictionary<int, int>();
  64. m_client = client;
  65. m_assetCache = pAssetCache;
  66. if (pAssetCache != null)
  67. m_missingsubstitute = pAssetCache.Get("5748decc-f629-461c-9a36-a35a221fe21f");
  68. else
  69. m_log.Error("[ClientView] - couldn't set missing image, all manner of things will probably break");
  70. m_j2kDecodeModule = pJ2kDecodeModule;
  71. }
  72. public void EnqueueReq(TextureRequestArgs newRequest)
  73. {
  74. //newRequest is the properties of our new texture fetch request.
  75. //Basically, here is where we queue up "new" requests..
  76. // .. or modify existing requests to suit.
  77. //Make sure we're not shutting down..
  78. if (!m_shuttingdown)
  79. {
  80. //Do we already know about this UUID?
  81. if (m_imagestore.ContainsKey(newRequest.RequestedAssetID))
  82. {
  83. //Check the packet sequence to make sure this isn't older than
  84. //one we've already received
  85. J2KImage imgrequest = m_imagestore[newRequest.RequestedAssetID];
  86. // This is the inbound request sequence number. We can ignore
  87. // "old" ones.
  88. if (newRequest.requestSequence > imgrequest.m_lastSequence)
  89. {
  90. imgrequest.m_lastSequence = newRequest.requestSequence;
  91. //Check the priority
  92. double priority = imgrequest.m_requestedPriority;
  93. if (priority != newRequest.Priority)
  94. {
  95. //Remove the old priority
  96. m_priorities.Remove(imgrequest.m_designatedPriorityKey);
  97. //Assign a new unique priority
  98. imgrequest.m_requestedPriority = newRequest.Priority;
  99. imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
  100. }
  101. //Update the requested discard level
  102. imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
  103. //Update the requested packet number
  104. imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
  105. //Check if this will create an outstanding texture request
  106. bool activated = imgrequest.m_completedSendAtCurrentDiscardLevel;
  107. //Run an update
  108. imgrequest.RunUpdate();
  109. if (activated && !imgrequest.m_completedSendAtCurrentDiscardLevel && imgrequest.m_decoded)
  110. {
  111. Interlocked.Increment(ref m_outstandingtextures);
  112. }
  113. }
  114. }
  115. else
  116. {
  117. J2KImage imgrequest = new J2KImage(this);
  118. //Assign our missing substitute
  119. imgrequest.m_MissingSubstitute = m_missingsubstitute;
  120. //Assign our decoder module
  121. imgrequest.m_j2kDecodeModule = m_j2kDecodeModule;
  122. //Assign our asset cache module
  123. imgrequest.m_assetCache = m_assetCache;
  124. //Assign a priority based on our request
  125. imgrequest.m_designatedPriorityKey = AssignPriority(newRequest.RequestedAssetID, newRequest.Priority);
  126. //Assign the requested discard level
  127. imgrequest.m_requestedDiscardLevel = newRequest.DiscardLevel;
  128. //Assign the requested packet number
  129. imgrequest.m_requestedPacketNumber = newRequest.PacketNumber;
  130. //Assign the requested priority
  131. imgrequest.m_requestedPriority = newRequest.Priority;
  132. //Assign the asset uuid
  133. imgrequest.m_requestedUUID = newRequest.RequestedAssetID;
  134. m_imagestore.Add(imgrequest.m_requestedUUID, imgrequest);
  135. //Run an update
  136. imgrequest.RunUpdate();
  137. }
  138. }
  139. }
  140. private double AssignPriority(UUID pAssetID, double pPriority)
  141. {
  142. //First, find out if we can just assign directly
  143. if (m_priorityresolver.ContainsKey((int)pPriority) == false)
  144. {
  145. m_priorities.Add((double)((int)pPriority), pAssetID);
  146. m_priorityresolver.Add((int)pPriority, 0);
  147. return (double)((int)pPriority);
  148. }
  149. else
  150. {
  151. //Use the hash lookup goodness of a secondary dictionary to find a free slot
  152. double mFreePriority = ((int)pPriority) + (doubleMinimum * (m_priorityresolver[(int)pPriority] + 1));
  153. m_priorities[mFreePriority] = pAssetID;
  154. m_priorityresolver[(int)pPriority]++;
  155. return mFreePriority;
  156. }
  157. }
  158. public bool ProcessImageQueue(int count, int maxpack)
  159. {
  160. // this can happen during Close()
  161. if (m_client == null)
  162. return false;
  163. //Count is the number of textures we want to process in one go.
  164. //As part of this class re-write, that number will probably rise
  165. //since we're processing in a more efficient manner.
  166. int numCollected = 0;
  167. //Calculate our threshold
  168. int threshold;
  169. if (m_lastloopprocessed == 0)
  170. {
  171. if (m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null || m_client.PacketHandler.PacketQueue.TextureThrottle == null)
  172. return false;
  173. //This is decent for a semi fast machine, but we'll calculate it more accurately based on time below
  174. threshold = m_client.PacketHandler.PacketQueue.TextureThrottle.Current / 6300;
  175. m_lastloopprocessed = DateTime.Now.Ticks;
  176. }
  177. else
  178. {
  179. double throttleseconds = ((double)DateTime.Now.Ticks - (double)m_lastloopprocessed) / (double)TimeSpan.TicksPerSecond;
  180. throttleseconds = throttleseconds * m_client.PacketHandler.PacketQueue.TextureThrottle.Current;
  181. //Average of 1000 bytes per packet
  182. throttleseconds = throttleseconds / 1000;
  183. //Safe-zone multiplier of 2.0
  184. threshold = (int)(throttleseconds * 2.0);
  185. m_lastloopprocessed = DateTime.Now.Ticks;
  186. }
  187. if (threshold < 10)
  188. {
  189. threshold = 10;
  190. }
  191. if (m_client.PacketHandler == null)
  192. return false;
  193. if (m_client.PacketHandler.PacketQueue == null)
  194. return false;
  195. //First of all make sure our packet queue isn't above our threshold
  196. //Uncomment this to see what the texture stack is doing
  197. //m_log.Debug("Queue: " + m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount.ToString() + " Threshold: " + threshold.ToString() + " outstanding: " + m_outstandingtextures.ToString());
  198. if (m_client.PacketHandler.PacketQueue.TextureOutgoingPacketQueueCount < threshold && m_outstandingtextures > 0)
  199. {
  200. bool justreset = false;
  201. for (int x = m_priorities.Count - 1; x > -1; x--)
  202. {
  203. J2KImage imagereq = m_imagestore[m_priorities.Values[x]];
  204. if (imagereq.m_decoded == true && !imagereq.m_completedSendAtCurrentDiscardLevel)
  205. {
  206. numCollected++;
  207. //SendPackets will send up to ten packets per cycle
  208. if (imagereq.SendPackets(m_client, maxpack))
  209. {
  210. //Send complete
  211. if (!imagereq.m_completedSendAtCurrentDiscardLevel)
  212. {
  213. imagereq.m_completedSendAtCurrentDiscardLevel = true;
  214. Interlocked.Decrement(ref m_outstandingtextures);
  215. //Re-assign priority to bottom
  216. //Remove the old priority
  217. m_priorities.Remove(imagereq.m_designatedPriorityKey);
  218. int lowest;
  219. if (m_priorities.Count > 0)
  220. {
  221. lowest = (int)m_priorities.Keys[0];
  222. lowest--;
  223. }
  224. else
  225. {
  226. lowest = -10000;
  227. }
  228. m_priorities.Add((double)lowest, imagereq.m_requestedUUID);
  229. imagereq.m_designatedPriorityKey = (double)lowest;
  230. if (m_priorityresolver.ContainsKey((int)lowest))
  231. {
  232. m_priorityresolver[(int)lowest]++;
  233. }
  234. else
  235. {
  236. m_priorityresolver.Add((int)lowest, 0);
  237. }
  238. }
  239. }
  240. if (numCollected == count)
  241. {
  242. break;
  243. }
  244. }
  245. if (numCollected == count || m_outstandingtextures == 0)
  246. break;
  247. if (numCollected % m_outstandingtextures == 0 && !justreset)
  248. {
  249. //We've gotten as much as we can from the stack,
  250. //reset to the top so that we can send MOAR DATA (nomnomnom)!
  251. x = m_priorities.Count - 1;
  252. justreset = true; //prevents us from getting stuck in a loop
  253. }
  254. }
  255. }
  256. return m_outstandingtextures != 0;
  257. }
  258. //Faux destructor
  259. public void Close()
  260. {
  261. m_shuttingdown = true;
  262. m_j2kDecodeModule = null;
  263. m_assetCache = null;
  264. m_client = null;
  265. }
  266. }
  267. }