ConstantBuffer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using System.Collections.Generic;
  2. using Unity.Collections.LowLevel.Unsafe;
  3. namespace UnityEngine.Rendering
  4. {
  5. /// <summary>
  6. /// Constant Buffer management class.
  7. /// </summary>
  8. public class ConstantBuffer
  9. {
  10. static List<ConstantBufferBase> m_RegisteredConstantBuffers = new List<ConstantBufferBase>();
  11. /// <summary>
  12. /// Update the GPU data of the constant buffer and bind it globally.
  13. /// </summary>
  14. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  15. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  16. /// <param name="data">Input data of the constant buffer.</param>
  17. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  18. public static void PushGlobal<CBType>(CommandBuffer cmd, in CBType data, int shaderId) where CBType : struct
  19. {
  20. var cb = ConstantBufferSingleton<CBType>.instance;
  21. cb.UpdateData(cmd, data);
  22. cb.SetGlobal(cmd, shaderId);
  23. }
  24. /// <summary>
  25. /// Update the GPU data of the constant buffer and bind it to a compute shader.
  26. /// </summary>
  27. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  28. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  29. /// <param name="data">Input data of the constant buffer.</param>
  30. /// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
  31. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  32. public static void Push<CBType>(CommandBuffer cmd, in CBType data, ComputeShader cs, int shaderId) where CBType : struct
  33. {
  34. var cb = ConstantBufferSingleton<CBType>.instance;
  35. cb.UpdateData(cmd, data);
  36. cb.Set(cmd, cs, shaderId);
  37. }
  38. /// <summary>
  39. /// Update the GPU data of the constant buffer and bind it to a material.
  40. /// </summary>
  41. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  42. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  43. /// <param name="data">Input data of the constant buffer.</param>
  44. /// <param name="mat">Material to which the constant buffer should be bound.</param>
  45. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  46. public static void Push<CBType>(CommandBuffer cmd, in CBType data, Material mat, int shaderId) where CBType : struct
  47. {
  48. var cb = ConstantBufferSingleton<CBType>.instance;
  49. cb.UpdateData(cmd, data);
  50. cb.Set(mat, shaderId);
  51. }
  52. /// <summary>
  53. /// Update the GPU data of the constant buffer.
  54. /// </summary>
  55. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  56. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  57. /// <param name="data">Input data of the constant buffer.</param>
  58. public static void UpdateData<CBType>(CommandBuffer cmd, in CBType data) where CBType : struct
  59. {
  60. var cb = ConstantBufferSingleton<CBType>.instance;
  61. cb.UpdateData(cmd, data);
  62. }
  63. /// <summary>
  64. /// Bind the constant buffer globally.
  65. /// </summary>
  66. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  67. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  68. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  69. public static void SetGlobal<CBType>(CommandBuffer cmd, int shaderId) where CBType : struct
  70. {
  71. var cb = ConstantBufferSingleton<CBType>.instance;
  72. cb.SetGlobal(cmd, shaderId);
  73. }
  74. /// <summary>
  75. /// Bind the constant buffer to a compute shader.
  76. /// </summary>
  77. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  78. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  79. /// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
  80. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  81. public static void Set<CBType>(CommandBuffer cmd, ComputeShader cs, int shaderId) where CBType : struct
  82. {
  83. var cb = ConstantBufferSingleton<CBType>.instance;
  84. cb.Set(cmd, cs, shaderId);
  85. }
  86. /// <summary>
  87. /// Bind the constant buffer to a material.
  88. /// </summary>
  89. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  90. /// <param name="mat">Material to which the constant buffer should be bound.</param>
  91. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  92. public static void Set<CBType>(Material mat, int shaderId) where CBType : struct
  93. {
  94. var cb = ConstantBufferSingleton<CBType>.instance;
  95. cb.Set(mat, shaderId);
  96. }
  97. /// <summary>
  98. /// Release all currently allocated singleton constant buffers.
  99. /// This needs to be called before shutting down the application.
  100. /// </summary>
  101. public static void ReleaseAll()
  102. {
  103. foreach (var cb in m_RegisteredConstantBuffers)
  104. cb.Release();
  105. m_RegisteredConstantBuffers.Clear();
  106. }
  107. internal static void Register(ConstantBufferBase cb)
  108. {
  109. m_RegisteredConstantBuffers.Add(cb);
  110. }
  111. }
  112. /// <summary>
  113. /// The base class of Constant Buffer.
  114. /// </summary>
  115. public abstract class ConstantBufferBase
  116. {
  117. /// <summary>
  118. /// Release the constant buffer.
  119. /// </summary>
  120. public abstract void Release();
  121. }
  122. /// <summary>
  123. /// An instance of a constant buffer.
  124. /// </summary>
  125. /// <typeparam name="CBType">The type of structure representing the constant buffer data.</typeparam>
  126. public class ConstantBuffer<CBType> : ConstantBufferBase where CBType : struct
  127. {
  128. // Used to track all global bindings used by this CB type.
  129. HashSet<int> m_GlobalBindings = new HashSet<int>();
  130. // Array is required by the ComputeBuffer SetData API
  131. CBType[] m_Data = new CBType[1];
  132. ComputeBuffer m_GPUConstantBuffer = null;
  133. /// <summary>
  134. /// Constant Buffer constructor.
  135. /// </summary>
  136. public ConstantBuffer()
  137. {
  138. m_GPUConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf<CBType>(), ComputeBufferType.Constant);
  139. }
  140. /// <summary>
  141. /// Update the GPU data of the constant buffer.
  142. /// </summary>
  143. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  144. /// <param name="data">Input data of the constant buffer.</param>
  145. public void UpdateData(CommandBuffer cmd, in CBType data)
  146. {
  147. m_Data[0] = data;
  148. #if UNITY_2021_1_OR_NEWER
  149. cmd.SetBufferData(m_GPUConstantBuffer, m_Data);
  150. #else
  151. cmd.SetComputeBufferData(m_GPUConstantBuffer, m_Data);
  152. #endif
  153. }
  154. /// <summary>
  155. /// Bind the constant buffer globally.
  156. /// </summary>
  157. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  158. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  159. public void SetGlobal(CommandBuffer cmd, int shaderId)
  160. {
  161. m_GlobalBindings.Add(shaderId);
  162. cmd.SetGlobalConstantBuffer(m_GPUConstantBuffer, shaderId, 0, m_GPUConstantBuffer.stride);
  163. }
  164. /// <summary>
  165. /// Bind the constant buffer to a compute shader.
  166. /// </summary>
  167. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  168. /// <param name="cs">Compute shader to which the constant buffer should be bound.</param>
  169. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  170. public void Set(CommandBuffer cmd, ComputeShader cs, int shaderId)
  171. {
  172. cmd.SetComputeConstantBufferParam(cs, shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
  173. }
  174. /// <summary>
  175. /// Bind the constant buffer to a material.
  176. /// </summary>
  177. /// <param name="mat">Material to which the constant buffer should be bound.</param>
  178. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  179. public void Set(Material mat, int shaderId)
  180. {
  181. // This isn't done via command buffer because as long as the buffer itself is not destroyed,
  182. // the binding stays valid. Only the commit of data needs to go through the command buffer.
  183. // We do it here anyway for now to simplify user API.
  184. mat.SetConstantBuffer(shaderId, m_GPUConstantBuffer, 0, m_GPUConstantBuffer.stride);
  185. }
  186. /// <summary>
  187. /// Update the GPU data of the constant buffer and bind it globally.
  188. /// </summary>
  189. /// <param name="cmd">Command Buffer used to execute the graphic commands.</param>
  190. /// <param name="data">Input data of the constant buffer.</param>
  191. /// <param name="shaderId">Shader porperty id to bind the constant buffer to.</param>
  192. public void PushGlobal(CommandBuffer cmd, in CBType data, int shaderId)
  193. {
  194. UpdateData(cmd, data);
  195. SetGlobal(cmd, shaderId);
  196. }
  197. /// <summary>
  198. /// Release the constant buffers.
  199. /// </summary>
  200. public override void Release()
  201. {
  202. // Depending on the device, globally bound buffers can leave stale "valid" shader ids pointing to a destroyed buffer.
  203. // In DX11 it does not cause issues but on Vulkan this will result in skipped drawcalls (even if the buffer is not actually accessed in the shader).
  204. // To avoid this kind of issues, it's good practice to "unbind" all globally bound buffers upon destruction.
  205. foreach (int shaderId in m_GlobalBindings)
  206. Shader.SetGlobalConstantBuffer(shaderId, (ComputeBuffer)null, 0, 0);
  207. m_GlobalBindings.Clear();
  208. CoreUtils.SafeRelease(m_GPUConstantBuffer);
  209. }
  210. }
  211. class ConstantBufferSingleton<CBType> : ConstantBuffer<CBType> where CBType : struct
  212. {
  213. static ConstantBufferSingleton<CBType> s_Instance = null;
  214. internal static ConstantBufferSingleton<CBType> instance
  215. {
  216. get
  217. {
  218. if (s_Instance == null)
  219. {
  220. s_Instance = new ConstantBufferSingleton<CBType>();
  221. ConstantBuffer.Register(s_Instance);
  222. }
  223. return s_Instance;
  224. }
  225. set
  226. {
  227. s_Instance = value;
  228. }
  229. }
  230. public override void Release()
  231. {
  232. base.Release();
  233. s_Instance = null;
  234. }
  235. }
  236. }