Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using System;
  2. using System.Text;
  3. using System.Diagnostics;
  4. using System.Collections.Generic;
  5. using Unity.Collections;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEngine.Rendering;
  8. using UnityEngine.Rendering.RenderGraphModule;
  9. namespace UnityEngine.Rendering.Universal
  10. {
  11. // a customized version of RenderGraphResourcePool from SRP core
  12. internal class RTHandleResourcePool
  13. {
  14. // Dictionary tracks resources by hash and stores resources with same hash in a List (list instead of a stack because we need to be able to remove stale allocations, potentially in the middle of the stack).
  15. // The list needs to be sorted otherwise you could get inconsistent resource usage from one frame to another.
  16. protected Dictionary<int, SortedList<int, (RTHandle resource, int frameIndex)>> m_ResourcePool = new Dictionary<int, SortedList<int, (RTHandle resource, int frameIndex)>>();
  17. protected List<int> m_RemoveList = new List<int>(32); // Used to remove stale resources as there is no RemoveAll on SortedLists
  18. protected static int s_CurrentStaleResourceCount = 0;
  19. // Keep stale resources alive for 3 frames
  20. protected static int s_StaleResourceLifetime = 3;
  21. // Store max 32 rtHandles
  22. // 1080p * 32bpp * 32 = 265.4mb
  23. protected static int s_StaleResourceMaxCapacity = 32;
  24. /// <summary>
  25. /// Controls the resource pool's max stale resource capacity.
  26. /// Increasing the capacity may have a negative impact on the memory usage.
  27. /// Increasing the capacity may reduce the runtime RTHandle realloc cost in multi view/multi camera setup.
  28. /// Setting capacity will purge the current pool. It is recommended to setup the capacity upfront and not changing it during the runtime.
  29. /// Setting capacity won't do anything if new capacity is the same to the current capacity.
  30. /// </summary>
  31. internal int staleResourceCapacity
  32. {
  33. get { return s_StaleResourceMaxCapacity; }
  34. set {
  35. if (s_StaleResourceMaxCapacity != value)
  36. {
  37. s_StaleResourceMaxCapacity = value;
  38. Cleanup();
  39. }
  40. }
  41. }
  42. // Add no longer used resouce to pool
  43. // Return true if resource is added to pool successfully, return false otherwise.
  44. internal bool AddResourceToPool(in TextureDesc texDesc, RTHandle resource, int currentFrameIndex)
  45. {
  46. if (s_CurrentStaleResourceCount >= s_StaleResourceMaxCapacity)
  47. return false;
  48. int hashCode = GetHashCodeWithNameHash(texDesc);
  49. if (!m_ResourcePool.TryGetValue(hashCode, out var list))
  50. {
  51. // Init list with max capacity to avoid runtime GC.Alloc when calling list.Add(resize list)
  52. list = new SortedList<int, (RTHandle resource, int frameIndex)>(s_StaleResourceMaxCapacity);
  53. m_ResourcePool.Add(hashCode, list);
  54. }
  55. list.Add(resource.GetInstanceID(), (resource, currentFrameIndex));
  56. s_CurrentStaleResourceCount++;
  57. return true;
  58. }
  59. // Get resource from the pool using TextureDesc as key
  60. // Return true if resource successfully retried resource from the pool, return false otherwise.
  61. internal bool TryGetResource(in TextureDesc texDesc, out RTHandle resource, bool usepool = true)
  62. {
  63. int hashCode = GetHashCodeWithNameHash(texDesc);
  64. if (usepool && m_ResourcePool.TryGetValue(hashCode, out SortedList<int, (RTHandle resource, int frameIndex)> list) && list.Count > 0)
  65. {
  66. resource = list.Values[list.Count - 1].resource;
  67. list.RemoveAt(list.Count - 1); // O(1) since it's the last element.
  68. s_CurrentStaleResourceCount--;
  69. return true;
  70. }
  71. resource = null;
  72. return false;
  73. }
  74. // Release all resources in pool.
  75. internal void Cleanup()
  76. {
  77. foreach (var kvp in m_ResourcePool)
  78. {
  79. foreach (var res in kvp.Value)
  80. {
  81. res.Value.resource.Release();
  82. }
  83. }
  84. m_ResourcePool.Clear();
  85. s_CurrentStaleResourceCount = 0;
  86. }
  87. static protected bool ShouldReleaseResource(int lastUsedFrameIndex, int currentFrameIndex)
  88. {
  89. // We need to have a delay of a few frames before releasing resources for good.
  90. // Indeed, when having multiple off-screen cameras, they are rendered in a separate SRP render call and thus with a different frame index than main camera
  91. // This causes texture to be deallocated/reallocated every frame if the two cameras don't need the same buffers.
  92. return (lastUsedFrameIndex + s_StaleResourceLifetime) < currentFrameIndex;
  93. }
  94. // Release resources that are not used in last couple frames.
  95. internal void PurgeUnusedResources(int currentFrameIndex)
  96. {
  97. // Update the frame index for the lambda. Static because we don't want to capture.
  98. m_RemoveList.Clear();
  99. foreach (var kvp in m_ResourcePool)
  100. {
  101. // WARNING: No foreach here. Sorted list GetEnumerator generates garbage...
  102. var list = kvp.Value;
  103. var keys = list.Keys;
  104. var values = list.Values;
  105. for (int i = 0; i < list.Count; ++i)
  106. {
  107. var value = values[i];
  108. if (ShouldReleaseResource(value.frameIndex, currentFrameIndex))
  109. {
  110. value.resource.Release();
  111. m_RemoveList.Add(keys[i]);
  112. s_CurrentStaleResourceCount--;
  113. }
  114. }
  115. foreach (var key in m_RemoveList)
  116. list.Remove(key);
  117. }
  118. }
  119. internal void LogDebugInfo()
  120. {
  121. var sb = new StringBuilder();
  122. sb.AppendFormat("RTHandleResourcePool for frame {0}, Total stale resources {1}", Time.frameCount, s_CurrentStaleResourceCount);
  123. sb.AppendLine();
  124. foreach (var kvp in m_ResourcePool)
  125. {
  126. var list = kvp.Value;
  127. var keys = list.Keys;
  128. var values = list.Values;
  129. for (int i = 0; i < list.Count; ++i)
  130. {
  131. var value = values[i];
  132. sb.AppendFormat("Resrouce in pool: Name {0} Last active frame index {1} Size {2} x {3} x {4}",
  133. value.resource.name,
  134. value.frameIndex,
  135. value.resource.rt.descriptor.width,
  136. value.resource.rt.descriptor.height,
  137. value.resource.rt.descriptor.volumeDepth
  138. );
  139. sb.AppendLine();
  140. }
  141. }
  142. Debug.Log(sb);
  143. }
  144. // NOTE: Only allow reusing resource with the same name.
  145. // This is because some URP code uses texture name as key to bind input texture (GBUFFER_2). Different name will result in URP bind texture to different shader input slot.
  146. // Ideally if URP code uses shaderPropertyID(instead of name string), we can relax the restriction here.
  147. internal int GetHashCodeWithNameHash(in TextureDesc texDesc)
  148. {
  149. int hashCode = texDesc.GetHashCode();
  150. hashCode = hashCode * 23 + texDesc.name.GetHashCode();
  151. return hashCode;
  152. }
  153. internal static TextureDesc CreateTextureDesc(RenderTextureDescriptor desc,
  154. TextureSizeMode textureSizeMode = TextureSizeMode.Explicit, int anisoLevel = 1, float mipMapBias = 0,
  155. FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Clamp, string name = "")
  156. {
  157. TextureDesc rgDesc = new TextureDesc(desc.width, desc.height);
  158. rgDesc.sizeMode = textureSizeMode;
  159. rgDesc.slices = desc.volumeDepth;
  160. rgDesc.depthBufferBits = (DepthBits)desc.depthBufferBits;
  161. rgDesc.colorFormat = desc.graphicsFormat;
  162. rgDesc.filterMode = filterMode;
  163. rgDesc.wrapMode = wrapMode;
  164. rgDesc.dimension = desc.dimension;
  165. rgDesc.enableRandomWrite = desc.enableRandomWrite;
  166. rgDesc.useMipMap = desc.useMipMap;
  167. rgDesc.autoGenerateMips = desc.autoGenerateMips;
  168. rgDesc.isShadowMap = desc.shadowSamplingMode != ShadowSamplingMode.None;
  169. rgDesc.anisoLevel = anisoLevel;
  170. rgDesc.mipMapBias = mipMapBias;
  171. rgDesc.msaaSamples = (MSAASamples)desc.msaaSamples;
  172. rgDesc.bindTextureMS = desc.bindMS;
  173. rgDesc.useDynamicScale = desc.useDynamicScale;
  174. rgDesc.memoryless = RenderTextureMemoryless.None;
  175. rgDesc.vrUsage = VRTextureUsage.None;
  176. rgDesc.name = name;
  177. return rgDesc;
  178. }
  179. }
  180. }