TextureLerper.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. using System.Collections.Generic;
  2. using UnityEngine.Assertions;
  3. namespace UnityEngine.Rendering.PostProcessing
  4. {
  5. class TextureLerper
  6. {
  7. static TextureLerper m_Instance;
  8. internal static TextureLerper instance
  9. {
  10. get
  11. {
  12. if (m_Instance == null)
  13. m_Instance = new TextureLerper();
  14. return m_Instance;
  15. }
  16. }
  17. CommandBuffer m_Command;
  18. PropertySheetFactory m_PropertySheets;
  19. PostProcessResources m_Resources;
  20. List<RenderTexture> m_Recycled;
  21. List<RenderTexture> m_Actives;
  22. TextureLerper()
  23. {
  24. m_Recycled = new List<RenderTexture>();
  25. m_Actives = new List<RenderTexture>();
  26. }
  27. internal void BeginFrame(PostProcessRenderContext context)
  28. {
  29. m_Command = context.command;
  30. m_PropertySheets = context.propertySheets;
  31. m_Resources = context.resources;
  32. }
  33. internal void EndFrame()
  34. {
  35. // Release any remaining RT in the recycled list
  36. if (m_Recycled.Count > 0)
  37. {
  38. foreach (var rt in m_Recycled)
  39. RuntimeUtilities.Destroy(rt);
  40. m_Recycled.Clear();
  41. }
  42. // There's a high probability that RTs will be requested in the same order on next
  43. // frame so keep them in the same order
  44. if (m_Actives.Count > 0)
  45. {
  46. foreach (var rt in m_Actives)
  47. m_Recycled.Add(rt);
  48. m_Actives.Clear();
  49. }
  50. }
  51. RenderTexture Get(RenderTextureFormat format, int w, int h, int d = 1, bool enableRandomWrite = false, bool force3D = false)
  52. {
  53. RenderTexture rt = null;
  54. int i, len = m_Recycled.Count;
  55. for (i = 0; i < len; i++)
  56. {
  57. var r = m_Recycled[i];
  58. if (r.width == w && r.height == h && r.volumeDepth == d && r.format == format && r.enableRandomWrite == enableRandomWrite && (!force3D || (r.dimension == TextureDimension.Tex3D)))
  59. {
  60. rt = r;
  61. break;
  62. }
  63. }
  64. if (rt == null)
  65. {
  66. var dimension = (d > 1) || force3D
  67. ? TextureDimension.Tex3D
  68. : TextureDimension.Tex2D;
  69. rt = new RenderTexture(w, h, 0, format)
  70. {
  71. dimension = dimension,
  72. filterMode = FilterMode.Bilinear,
  73. wrapMode = TextureWrapMode.Clamp,
  74. anisoLevel = 0,
  75. volumeDepth = d,
  76. enableRandomWrite = enableRandomWrite
  77. };
  78. rt.Create();
  79. }
  80. else m_Recycled.RemoveAt(i);
  81. m_Actives.Add(rt);
  82. return rt;
  83. }
  84. internal Texture Lerp(Texture from, Texture to, float t)
  85. {
  86. Assert.IsNotNull(from);
  87. Assert.IsNotNull(to);
  88. Assert.AreEqual(from.width, to.width);
  89. Assert.AreEqual(from.height, to.height);
  90. // Saves a potentially expensive fullscreen blit when using dirt textures & the likes
  91. if (from == to)
  92. return from;
  93. // Don't need to lerp boundary conditions
  94. if (t <= 0f) return from;
  95. if (t >= 1f) return to;
  96. bool is3D = from is Texture3D
  97. || (from is RenderTexture && ((RenderTexture)from).volumeDepth > 1);
  98. RenderTexture rt;
  99. // 3D texture blending is a special case and only works on compute enabled platforms
  100. if (is3D)
  101. {
  102. int dpth = @from is Texture3D ? ((Texture3D) @from).depth : ((RenderTexture) @from).volumeDepth;
  103. int size = Mathf.Max(from.width, from.height);
  104. size = Mathf.Max(size, dpth);
  105. rt = Get(RenderTextureFormat.ARGBHalf, from.width, from.height, dpth, true, true);
  106. var compute = m_Resources.computeShaders.texture3dLerp;
  107. int kernel = compute.FindKernel("KTexture3DLerp");
  108. m_Command.SetComputeVectorParam(compute, "_DimensionsAndLerp", new Vector4(from.width, from.height, dpth, t));
  109. m_Command.SetComputeTextureParam(compute, kernel, "_Output", rt);
  110. m_Command.SetComputeTextureParam(compute, kernel, "_From", from);
  111. m_Command.SetComputeTextureParam(compute, kernel, "_To", to);
  112. uint tgsX, tgsY, tgsZ;
  113. compute.GetKernelThreadGroupSizes(kernel, out tgsX, out tgsY, out tgsZ);
  114. Assert.AreEqual(tgsX, tgsY);
  115. int groupSizeXY = Mathf.CeilToInt(size / (float)tgsX);
  116. int groupSizeZ = Mathf.CeilToInt(size / (float)tgsZ);
  117. m_Command.DispatchCompute(compute, kernel, groupSizeXY, groupSizeXY, groupSizeZ);
  118. return rt;
  119. }
  120. // 2D texture blending
  121. // We could handle textures with different sizes by picking the biggest one to avoid
  122. // popping effects. This would work in most cases but will still pop if one texture is
  123. // wider but shorter than the other. Generally speaking you're expected to use same-size
  124. // textures anyway so we decided not to handle this case at the moment, especially since
  125. // it would waste a lot of texture memory as soon as you start using bigger textures
  126. // (snow ball effect).
  127. var format = TextureFormatUtilities.GetUncompressedRenderTextureFormat(to);
  128. rt = Get(format, to.width, to.height);
  129. var sheet = m_PropertySheets.Get(m_Resources.shaders.texture2dLerp);
  130. sheet.properties.SetTexture(ShaderIDs.To, to);
  131. sheet.properties.SetFloat(ShaderIDs.Interp, t);
  132. m_Command.BlitFullscreenTriangle(from, rt, sheet, 0);
  133. return rt;
  134. }
  135. internal Texture Lerp(Texture from, Color to, float t)
  136. {
  137. Assert.IsNotNull(from);
  138. if (t < 0.00001)
  139. return from;
  140. bool is3D = from is Texture3D
  141. || (from is RenderTexture && ((RenderTexture)from).volumeDepth > 1);
  142. RenderTexture rt;
  143. // 3D texture blending is a special case and only works on compute enabled platforms
  144. if (is3D)
  145. {
  146. int dpth = @from is Texture3D ? ((Texture3D) @from).depth : ((RenderTexture) @from).volumeDepth;
  147. int size = Mathf.Max(from.width, from.height);
  148. size = Mathf.Max(size, dpth);
  149. rt = Get(RenderTextureFormat.ARGBHalf, from.width, from.height, dpth, true, true);
  150. var compute = m_Resources.computeShaders.texture3dLerp;
  151. int kernel = compute.FindKernel("KTexture3DLerpToColor");
  152. m_Command.SetComputeVectorParam(compute, "_DimensionsAndLerp", new Vector4(from.width, from.height, dpth, t));
  153. m_Command.SetComputeVectorParam(compute, "_TargetColor", new Vector4(to.r, to.g, to.b, to.a));
  154. m_Command.SetComputeTextureParam(compute, kernel, "_Output", rt);
  155. m_Command.SetComputeTextureParam(compute, kernel, "_From", from);
  156. int groupSize = Mathf.CeilToInt(size / 4f);
  157. m_Command.DispatchCompute(compute, kernel, groupSize, groupSize, groupSize);
  158. return rt;
  159. }
  160. // 2D texture blending
  161. // We could handle textures with different sizes by picking the biggest one to avoid
  162. // popping effects. This would work in most cases but will still pop if one texture is
  163. // wider but shorter than the other. Generally speaking you're expected to use same-size
  164. // textures anyway so we decided not to handle this case at the moment, especially since
  165. // it would waste a lot of texture memory as soon as you start using bigger textures
  166. // (snow ball effect).
  167. var format = TextureFormatUtilities.GetUncompressedRenderTextureFormat(from);
  168. rt = Get(format, from.width, from.height);
  169. var sheet = m_PropertySheets.Get(m_Resources.shaders.texture2dLerp);
  170. sheet.properties.SetVector(ShaderIDs.TargetColor, new Vector4(to.r, to.g, to.b, to.a));
  171. sheet.properties.SetFloat(ShaderIDs.Interp, t);
  172. m_Command.BlitFullscreenTriangle(from, rt, sheet, 1);
  173. return rt;
  174. }
  175. internal void Clear()
  176. {
  177. foreach (var rt in m_Actives)
  178. RuntimeUtilities.Destroy(rt);
  179. foreach (var rt in m_Recycled)
  180. RuntimeUtilities.Destroy(rt);
  181. m_Actives.Clear();
  182. m_Recycled.Clear();
  183. }
  184. }
  185. }