Sin descripción
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BlitRendererFeature.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using System;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Rendering.RenderGraphModule;
  5. using UnityEngine.Rendering;
  6. using UnityEngine.Rendering.Universal;
  7. // Example of how Blit operatrions can be handled using frameData using multiple ScriptaleRenderPasses.
  8. public class BlitRendererFeature : ScriptableRendererFeature
  9. {
  10. // The class living in frameData. It will take care of managing the texture resources.
  11. public class BlitData : ContextItem, IDisposable
  12. {
  13. // Textures used for the blit operations.
  14. RTHandle m_TextureFront;
  15. RTHandle m_TextureBack;
  16. // Render graph texture handles.
  17. TextureHandle m_TextureHandleFront;
  18. TextureHandle m_TextureHandleBack;
  19. // Scale bias is used to control how the blit operation is done. The x and y parameter controls the scale
  20. // and z and w controls the offset.
  21. static Vector4 scaleBias = new Vector4(1f, 1f, 0f, 0f);
  22. // Bool to manage which texture is the most resent.
  23. bool m_IsFront = true;
  24. // The texture which contains the color buffer from the most resent blit operation.
  25. public TextureHandle texture;
  26. // Function used to initialize BlitDatat. Should be called before starting to use the class for each frame.
  27. public void Init(RenderGraph renderGraph, RenderTextureDescriptor targetDescriptor, string textureName = null)
  28. {
  29. // Checks if the texture name is valid and puts in default value if not.
  30. var texName = String.IsNullOrEmpty(textureName) ? "_BlitTextureData" : textureName;
  31. // Reallocate if the RTHandles are being initialized for the first time or if the targetDescriptor has changed since last frame.
  32. RenderingUtils.ReAllocateHandleIfNeeded(ref m_TextureFront, targetDescriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: texName + "Front");
  33. RenderingUtils.ReAllocateHandleIfNeeded(ref m_TextureBack, targetDescriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: texName + "Back");
  34. // Create the texture handles inside render graph by importing the RTHandles in render graph.
  35. m_TextureHandleFront = renderGraph.ImportTexture(m_TextureFront);
  36. m_TextureHandleBack = renderGraph.ImportTexture(m_TextureBack);
  37. // Sets the active texture to the front buffer
  38. texture = m_TextureHandleFront;
  39. }
  40. // We will need to reset the texture handle after each frame to avoid leaking invalid texture handles
  41. // since the texture handles only lives for one frame.
  42. public override void Reset()
  43. {
  44. // Resets the color buffers to avoid carrying invalid references to the next frame.
  45. // This could be BlitData texture handles from last frame which will now be invalid.
  46. m_TextureHandleFront = TextureHandle.nullHandle;
  47. m_TextureHandleBack = TextureHandle.nullHandle;
  48. texture = TextureHandle.nullHandle;
  49. // Reset the acrive texture to be the front buffer.
  50. m_IsFront = true;
  51. }
  52. // The data we use to transfer data to the render function.
  53. class PassData
  54. {
  55. // When makeing a blit operation we will need a source, a destination and a material.
  56. // The source and destination is used to know where to copy from and to.
  57. public TextureHandle source;
  58. public TextureHandle destination;
  59. // The material is used to transform the color buffer while copying.
  60. public Material material;
  61. }
  62. // For this function we don't take a material as argument to show that we should remember to reset values
  63. // we don't use to avoid leaking values from last frame.
  64. public void RecordBlitColor(RenderGraph renderGraph, ContextContainer frameData)
  65. {
  66. // Check if BlitData's texture is valid if it isn't initialize BlitData.
  67. if (!texture.IsValid())
  68. {
  69. // Setup the descriptor we use for BlitData. We should use the camera target's descriptor as a start.
  70. var cameraData = frameData.Get<UniversalCameraData>();
  71. var descriptor = cameraData.cameraTargetDescriptor;
  72. // We disable MSAA for the blit operations.
  73. descriptor.msaaSamples = 1;
  74. // We disable the depth buffer, since we are only makeing transformations to the color buffer.
  75. descriptor.depthBufferBits = 0;
  76. Init(renderGraph, descriptor);
  77. }
  78. // Starts the recording of the render graph pass given the name of the pass
  79. // and outputting the data used to pass data to the execution of the render function.
  80. using (var builder = renderGraph.AddRasterRenderPass<PassData>("BlitColorPass", out var passData))
  81. {
  82. // Fetch UniversalResourceData from frameData to retrive the camera's active color attachment.
  83. var resourceData = frameData.Get<UniversalResourceData>();
  84. // Remember to reset material since it contains the value from last frame.
  85. // If we don't do this we would get the material last commited to the BlitPassData using RenderGraph
  86. // since we reuse the object allocation.
  87. passData.material = null;
  88. passData.source = resourceData.activeColorTexture;
  89. passData.destination = texture;
  90. // Sets input attachment to the cameras color buffer.
  91. builder.UseTexture(passData.source);
  92. // Sets output attachment 0 to BlitData's active texture.
  93. builder.SetRenderAttachment(passData.destination, 0);
  94. // Sets the render function.
  95. builder.SetRenderFunc((PassData passData, RasterGraphContext rgContext) => ExecutePass(passData, rgContext));
  96. }
  97. }
  98. // Records a render graph render pass which blits the BlitData's active texture back to the camera's color attachment.
  99. public void RecordBlitBackToColor(RenderGraph renderGraph, ContextContainer frameData)
  100. {
  101. // Check if BlitData's texture is valid if it isn't it hasn't been initialized or an error has occured.
  102. if (!texture.IsValid()) return;
  103. // Starts the recording of the render graph pass given the name of the pass
  104. // and outputting the data used to pass data to the execution of the render function.
  105. using (var builder = renderGraph.AddRasterRenderPass<PassData>($"BlitBackToColorPass", out var passData))
  106. {
  107. // Fetch UniversalResourceData from frameData to retrive the camera's active color attachment.
  108. var resourceData = frameData.Get<UniversalResourceData>();
  109. // Remember to reset material. Otherwise you would use the last material used in RecordFullScreenPass.
  110. passData.material = null;
  111. passData.source = texture;
  112. passData.destination = resourceData.activeColorTexture;
  113. // Sets input attachment to BitData's active texture.
  114. builder.UseTexture(passData.source);
  115. // Sets output attachment 0 to the cameras color buffer.
  116. builder.SetRenderAttachment(passData.destination, 0);
  117. // Sets the render function.
  118. builder.SetRenderFunc((PassData passData, RasterGraphContext rgContext) => ExecutePass(passData, rgContext));
  119. }
  120. }
  121. // This function blits the whole screen for a given material.
  122. public void RecordFullScreenPass(RenderGraph renderGraph, string passName, Material material)
  123. {
  124. // Checks if the data is previously initialized and if the material is valid.
  125. if (!texture.IsValid() || material == null)
  126. {
  127. Debug.LogWarning("Invalid input texture handle, will skip fullscreen pass.");
  128. return;
  129. }
  130. // Starts the recording of the render graph pass given the name of the pass
  131. // and outputting the data used to pass data to the execution of the render function.
  132. using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData))
  133. {
  134. // Switching the active texture handles to avoid blit. If we want the most recent
  135. // texture we can simply look at the variable texture
  136. m_IsFront = !m_IsFront;
  137. // Setting data to be used when executing the render function.
  138. passData.material = material;
  139. passData.source = texture;
  140. // Swap the active texture.
  141. if (m_IsFront)
  142. passData.destination = m_TextureHandleFront;
  143. else
  144. passData.destination = m_TextureHandleBack;
  145. // Sets input attachment to BlitData's old active texture.
  146. builder.UseTexture(passData.source);
  147. // Sets output attachment 0 to BitData's new active texture.
  148. builder.SetRenderAttachment(passData.destination, 0);
  149. // Update the texture after switching.
  150. texture = passData.destination;
  151. // Sets the render function.
  152. builder.SetRenderFunc((PassData passData, RasterGraphContext rgContext) => ExecutePass(passData, rgContext));
  153. }
  154. }
  155. // ExecutePass is the render function for each of the blit render graph recordings.
  156. // This is good practice to avoid using variables outside of the lambda it is called from.
  157. // It is static to avoid using member variables which could cause unintended behaviour.
  158. static void ExecutePass(PassData data, RasterGraphContext rgContext)
  159. {
  160. // We can use blit with or without a material both using the static scaleBias to avoid reallocations.
  161. if (data.material == null)
  162. Blitter.BlitTexture(rgContext.cmd, data.source, scaleBias, 0, false);
  163. else
  164. Blitter.BlitTexture(rgContext.cmd, data.source, scaleBias, data.material, 0);
  165. }
  166. // We need to release the textures once the renderer is released which will dispose every item inside
  167. // frameData (also data types previously created in earlier frames).
  168. public void Dispose()
  169. {
  170. m_TextureFront?.Release();
  171. m_TextureBack?.Release();
  172. }
  173. }
  174. // Initial render pass for the renderer feature which is run to initialize the data in frameData and copying
  175. // the camera's color attachment to a texture inside BlitData so we can do transformations using blit.
  176. class BlitStartRenderPass : ScriptableRenderPass
  177. {
  178. public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
  179. {
  180. // Creating the data BlitData inside frameData.
  181. var blitTextureData = frameData.Create<BlitData>();
  182. // Copies the camera's color attachment to a texture inside BlitData.
  183. blitTextureData.RecordBlitColor(renderGraph, frameData);
  184. }
  185. }
  186. // Render pass which makes a blit for each material given to the renderer feature.
  187. class BlitRenderPass : ScriptableRenderPass
  188. {
  189. List<Material> m_Materials;
  190. // Setup function used to retrive the materials from the renderer feature.
  191. public void Setup(List<Material> materials)
  192. {
  193. m_Materials = materials;
  194. }
  195. public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
  196. {
  197. // Retrives the BlitData from the current frame.
  198. var blitTextureData = frameData.Get<BlitData>();
  199. foreach(var material in m_Materials)
  200. {
  201. // Skip current cycle if the material is null since there is no need to blit if no
  202. // transformation happens.
  203. if (material == null) continue;
  204. // Records the material blit pass.
  205. blitTextureData.RecordFullScreenPass(renderGraph, $"Blit {material.name} Pass", material);
  206. }
  207. }
  208. }
  209. // Final render pass to copying the texture back to the camera's color attachment.
  210. class BlitEndRenderPass : ScriptableRenderPass
  211. {
  212. public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
  213. {
  214. // Retrives the BlitData from the current frame and blit it back again to the camera's color attachment.
  215. var blitTextureData = frameData.Get<BlitData>();
  216. blitTextureData.RecordBlitBackToColor(renderGraph, frameData);
  217. }
  218. }
  219. [SerializeField]
  220. [Tooltip("Materials used for blitting. They will be blit in the same order they have in the list starting from index 0. ")]
  221. List<Material> m_Materials;
  222. BlitStartRenderPass m_StartPass;
  223. BlitRenderPass m_BlitPass;
  224. BlitEndRenderPass m_EndPass;
  225. // Here you can create passes and do the initialization of them. This is called everytime serialization happens.
  226. public override void Create()
  227. {
  228. m_StartPass = new BlitStartRenderPass();
  229. m_BlitPass = new BlitRenderPass();
  230. m_EndPass = new BlitEndRenderPass();
  231. // Configures where the render pass should be injected.
  232. m_StartPass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
  233. m_BlitPass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
  234. m_EndPass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
  235. }
  236. // Here you can inject one or multiple render passes in the renderer.
  237. // This method is called when setting up the renderer once per-camera.
  238. public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
  239. {
  240. // Early return if there is no texture to blit.
  241. if (m_Materials == null || m_Materials.Count == 0) return;
  242. // Pass the material to the blit render pass.
  243. m_BlitPass.Setup(m_Materials);
  244. // Since they have the same RenderPassEvent the order matters when enqueueing them.
  245. renderer.EnqueuePass(m_StartPass);
  246. renderer.EnqueuePass(m_BlitPass);
  247. renderer.EnqueuePass(m_EndPass);
  248. }
  249. }