暫無描述
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.

ShaderDebugPrintManager.cs 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections;
  4. using Unity.Collections.LowLevel.Unsafe;
  5. using UnityEngine.Assertions;
  6. namespace UnityEngine.Rendering
  7. {
  8. /// <summary>
  9. /// Internal development tool.
  10. /// Manages gpu-buffers for shader debug printing.
  11. /// </summary>
  12. public sealed class ShaderDebugPrintManager
  13. {
  14. private static readonly ShaderDebugPrintManager s_Instance = new ShaderDebugPrintManager();
  15. private const int k_FramesInFlight = 4;
  16. private const int k_MaxBufferElements = 1024 * 16; // Must match the shader size definition
  17. private List<GraphicsBuffer> m_OutputBuffers = new List<GraphicsBuffer>();
  18. private List<Rendering.AsyncGPUReadbackRequest> m_ReadbackRequests =
  19. new List<Rendering.AsyncGPUReadbackRequest>();
  20. // Cache Action to avoid delegate allocation
  21. private Action<AsyncGPUReadbackRequest> m_BufferReadCompleteAction;
  22. private int m_FrameCounter = 0;
  23. private bool m_FrameCleared = false;
  24. private string m_OutputLine = "";
  25. private Action<string> m_OutputAction;
  26. private static readonly int m_ShaderPropertyIDInputMouse = Shader.PropertyToID("_ShaderDebugPrintInputMouse");
  27. private static readonly int m_ShaderPropertyIDInputFrame = Shader.PropertyToID("_ShaderDebugPrintInputFrame");
  28. private static readonly int m_shaderDebugOutputData = Shader.PropertyToID("shaderDebugOutputData");
  29. // A static "container" for all profiler markers.
  30. private static class Profiling
  31. {
  32. // Uses nameof to avoid aliasing
  33. public static readonly ProfilingSampler BufferReadComplete = new ProfilingSampler($"{nameof(ShaderDebugPrintManager)}.{nameof(BufferReadComplete)}");
  34. }
  35. // Should match: com.unity.render-pipelines.core/ShaderLibrary/ShaderDebugPrint.hlsl
  36. enum DebugValueType
  37. {
  38. TypeUint = 1,
  39. TypeInt = 2,
  40. TypeFloat = 3,
  41. TypeUint2 = 4,
  42. TypeInt2 = 5,
  43. TypeFloat2 = 6,
  44. TypeUint3 = 7,
  45. TypeInt3 = 8,
  46. TypeFloat3 = 9,
  47. TypeUint4 = 10,
  48. TypeInt4 = 11,
  49. TypeFloat4 = 12,
  50. TypeBool = 13,
  51. };
  52. private const uint k_TypeHasTag = 128;
  53. private int DebugValueTypeToElemSize(DebugValueType type)
  54. {
  55. switch (type)
  56. {
  57. case DebugValueType.TypeUint:
  58. case DebugValueType.TypeInt:
  59. case DebugValueType.TypeFloat:
  60. case DebugValueType.TypeBool:
  61. return 1;
  62. case DebugValueType.TypeUint2:
  63. case DebugValueType.TypeInt2:
  64. case DebugValueType.TypeFloat2:
  65. return 2;
  66. case DebugValueType.TypeUint3:
  67. case DebugValueType.TypeInt3:
  68. case DebugValueType.TypeFloat3:
  69. return 3;
  70. case DebugValueType.TypeUint4:
  71. case DebugValueType.TypeInt4:
  72. case DebugValueType.TypeFloat4:
  73. return 4;
  74. default:
  75. return 0;
  76. }
  77. }
  78. private ShaderDebugPrintManager()
  79. {
  80. for (int i = 0; i < k_FramesInFlight; i++)
  81. {
  82. m_OutputBuffers.Add(new GraphicsBuffer(GraphicsBuffer.Target.Structured, k_MaxBufferElements, 4));
  83. m_ReadbackRequests.Add(new Rendering.AsyncGPUReadbackRequest());
  84. }
  85. m_BufferReadCompleteAction = BufferReadComplete;
  86. m_OutputAction = DefaultOutput;
  87. }
  88. /// <summary>
  89. /// Get the current instance.
  90. /// </summary>
  91. public static ShaderDebugPrintManager instance => s_Instance;
  92. /// <summary>
  93. /// Set shader input constants.
  94. /// </summary>
  95. /// <param name="cmd">CommandBuffer to store the commands.</param>
  96. /// <param name="input">Input parameters for the constants.</param>
  97. public void SetShaderDebugPrintInputConstants(CommandBuffer cmd, ShaderDebugPrintInput input)
  98. {
  99. var mouse = new Vector4(input.pos.x, input.pos.y, input.leftDown ? 1 : 0, input.rightDown ? 1 : 0);
  100. cmd.SetGlobalVector(m_ShaderPropertyIDInputMouse, mouse);
  101. cmd.SetGlobalInt(m_ShaderPropertyIDInputFrame, m_FrameCounter);
  102. }
  103. /// <summary>
  104. /// Binds the gpu-buffers for current frame.
  105. /// </summary>
  106. /// <param name="cmd">CommandBuffer to store the commands.</param>
  107. public void SetShaderDebugPrintBindings(CommandBuffer cmd)
  108. {
  109. int index = m_FrameCounter % k_FramesInFlight;
  110. if (!m_ReadbackRequests[index].done)
  111. {
  112. // We shouldn't end up here too often
  113. m_ReadbackRequests[index].WaitForCompletion();
  114. }
  115. cmd.SetGlobalBuffer(m_shaderDebugOutputData, m_OutputBuffers[index]);
  116. ClearShaderDebugPrintBuffer();
  117. }
  118. private void ClearShaderDebugPrintBuffer()
  119. {
  120. // Only clear the buffer the first time this is called in each frame
  121. if (!m_FrameCleared)
  122. {
  123. int index = m_FrameCounter % k_FramesInFlight;
  124. NativeArray<uint> data = new NativeArray<uint>(1, Allocator.Temp);
  125. data[0] = 0;
  126. m_OutputBuffers[index].SetData(data, 0, 0, 1);
  127. m_FrameCleared = true;
  128. }
  129. }
  130. private void BufferReadComplete(Rendering.AsyncGPUReadbackRequest request)
  131. {
  132. using var profScope = new ProfilingScope(Profiling.BufferReadComplete);
  133. Assert.IsTrue(request.done);
  134. if (!request.hasError)
  135. {
  136. NativeArray<uint> data = request.GetData<uint>(0);
  137. uint count = data[0];
  138. if (count >= k_MaxBufferElements)
  139. {
  140. count = k_MaxBufferElements;
  141. // Shader print buffer is full, some data is lost!
  142. Debug.LogWarning("Debug Shader Print Buffer Full!");
  143. }
  144. string newOutputLine = "";
  145. if (count > 0)
  146. newOutputLine += "Frame #" + m_FrameCounter + ": ";
  147. unsafe // Need to do ugly casts via pointers
  148. {
  149. uint* ptr = (uint*)data.GetUnsafePtr();
  150. for (int i = 1; i < count;)
  151. {
  152. DebugValueType type = (DebugValueType)(data[i] & 0x0f);
  153. bool hasTag = (data[i] & k_TypeHasTag) == k_TypeHasTag;
  154. // ensure elem for tag after the header
  155. if (hasTag && i + 1 < count)
  156. {
  157. uint tagEncoded = data[i + 1];
  158. i++;
  159. for (int j = 0; j < 4; j++)
  160. {
  161. char c = (char)(tagEncoded & 255);
  162. // skip '\0', for low-level output (avoid string termination)
  163. if (c == 0)
  164. continue;
  165. newOutputLine += c;
  166. tagEncoded >>= 8;
  167. }
  168. newOutputLine += " ";
  169. }
  170. // ensure elem for payload after the header/tag
  171. int elemSize = DebugValueTypeToElemSize(type);
  172. if (i + elemSize > count)
  173. break;
  174. i++; // [i] == payload
  175. switch (type)
  176. {
  177. case DebugValueType.TypeUint:
  178. {
  179. newOutputLine += $"{data[i]}u";
  180. break;
  181. }
  182. case DebugValueType.TypeInt:
  183. {
  184. int valueInt = *(int*)&ptr[i];
  185. newOutputLine += valueInt;
  186. break;
  187. }
  188. case DebugValueType.TypeFloat:
  189. {
  190. float valueFloat = *(float*)&ptr[i];
  191. newOutputLine += $"{valueFloat}f";
  192. break;
  193. }
  194. case DebugValueType.TypeUint2:
  195. {
  196. uint* valueUint2 = &ptr[i];
  197. newOutputLine += $"uint2({valueUint2[0]}, {valueUint2[1]})";
  198. break;
  199. }
  200. case DebugValueType.TypeInt2:
  201. {
  202. int* valueInt2 = (int*)&ptr[i];
  203. newOutputLine += $"int2({valueInt2[0]}, {valueInt2[1]})";
  204. break;
  205. }
  206. case DebugValueType.TypeFloat2:
  207. {
  208. float* valueFloat2 = (float*)&ptr[i];
  209. newOutputLine += $"float2({valueFloat2[0]}, {valueFloat2[1]})";
  210. break;
  211. }
  212. case DebugValueType.TypeUint3:
  213. {
  214. uint* valueUint3 = &ptr[i];
  215. newOutputLine += $"uint3({valueUint3[0]}, {valueUint3[1]}, {valueUint3[2]})";
  216. break;
  217. }
  218. case DebugValueType.TypeInt3:
  219. {
  220. int* valueInt3 = (int*)&ptr[i];
  221. newOutputLine += $"int3({valueInt3[0]}, {valueInt3[1]}, {valueInt3[2]})";
  222. break;
  223. }
  224. case DebugValueType.TypeFloat3:
  225. {
  226. float* valueFloat3 = (float*)&ptr[i];
  227. newOutputLine += $"float3({valueFloat3[0]}, {valueFloat3[1]}, {valueFloat3[2]})";
  228. break;
  229. }
  230. case DebugValueType.TypeUint4:
  231. {
  232. uint* valueUint4 = &ptr[i];
  233. newOutputLine += $"uint4({valueUint4[0]}, {valueUint4[1]}, {valueUint4[2]}, {valueUint4[3]})";
  234. break;
  235. }
  236. case DebugValueType.TypeInt4:
  237. {
  238. int* valueInt4 = (int*)&ptr[i];
  239. newOutputLine += $"int4({valueInt4[0]}, {valueInt4[1]}, {valueInt4[2]}, {valueInt4[3]})";
  240. break;
  241. }
  242. case DebugValueType.TypeFloat4:
  243. {
  244. float* valueFloat4 = (float*)&ptr[i];
  245. newOutputLine += $"float4({valueFloat4[0]}, {valueFloat4[1]}, {valueFloat4[2]}, {valueFloat4[3]})";
  246. break;
  247. }
  248. case DebugValueType.TypeBool:
  249. {
  250. newOutputLine += ((data[i] == 0) ? "False" : "True");
  251. break;
  252. }
  253. default:
  254. i = (int)count; // Cannot handle the rest if there is an unknown type
  255. break;
  256. }
  257. i += elemSize;
  258. newOutputLine += " ";
  259. }
  260. }
  261. if (count > 0)
  262. {
  263. m_OutputLine = newOutputLine;
  264. m_OutputAction(newOutputLine);
  265. }
  266. }
  267. else
  268. {
  269. const string errorMsg = "Error at read back!";
  270. m_OutputLine = errorMsg;
  271. m_OutputAction(errorMsg);
  272. }
  273. }
  274. /// <summary>
  275. /// Initiate async read-back of the GPU buffer to the CPU.
  276. /// Prepare a new GPU buffer for the next frame.
  277. /// </summary>
  278. public void EndFrame()
  279. {
  280. int index = m_FrameCounter % k_FramesInFlight;
  281. m_ReadbackRequests[index] = Rendering.AsyncGPUReadback.Request(m_OutputBuffers[index], m_BufferReadCompleteAction);
  282. m_FrameCounter++;
  283. m_FrameCleared = false;
  284. }
  285. /// <summary>
  286. /// Initiate synchronous read-back of the GPU buffer to the CPU and executes output action. By default prints to the debug log.
  287. /// </summary>
  288. public void PrintImmediate()
  289. {
  290. int index = m_FrameCounter % k_FramesInFlight;
  291. var request = Rendering.AsyncGPUReadback.Request(m_OutputBuffers[index]);
  292. request.WaitForCompletion();
  293. m_BufferReadCompleteAction(request);
  294. m_FrameCounter++;
  295. m_FrameCleared = false;
  296. }
  297. // Custom output API
  298. /// <summary>
  299. /// Get current print line.
  300. /// </summary>
  301. public string outputLine { get => m_OutputLine; }
  302. /// <summary>
  303. /// Action taken for each print line. By default prints to the debug log.
  304. /// </summary>
  305. public Action<string> outputAction { set => m_OutputAction = value; }
  306. /// <summary>
  307. /// The default output action. Print to the debug log.
  308. /// </summary>
  309. /// <param name="line">Line to be printed.</param>
  310. public void DefaultOutput(string line)
  311. {
  312. Debug.Log(line);
  313. }
  314. }
  315. /// <summary>
  316. /// Shader constant input parameters.
  317. /// </summary>
  318. public struct ShaderDebugPrintInput
  319. {
  320. // Mouse input for the shader
  321. /// <summary>
  322. /// Mouse position.
  323. /// GameView bottom-left == (0,0) top-right == (surface.width, surface.height) where surface == game display surface/rendertarget
  324. /// For screen pixel coordinates, game-view should be set to "Free Aspect".
  325. /// Works only in PlayMode.
  326. /// </summary>
  327. public Vector2 pos { get; set; }
  328. /// <summary>
  329. /// Left mouse button is pressed.
  330. /// </summary>
  331. public bool leftDown { get; set; }
  332. /// <summary>
  333. /// Right mouse button is pressed.
  334. /// </summary>
  335. public bool rightDown { get; set; }
  336. /// <summary>
  337. /// Middle mouse button is pressed.
  338. /// </summary>
  339. public bool middleDown { get; set; }
  340. /// <summary>
  341. /// Pretty print parameters for debug purposes.
  342. /// </summary>
  343. /// <returns>A string containing debug information</returns>
  344. // NOTE: Separate from ToString on purpose.
  345. public string String()
  346. {
  347. return $"Mouse: {pos.x}x{pos.y} Btns: Left:{leftDown} Right:{rightDown} Middle:{middleDown} ";
  348. }
  349. }
  350. /// <summary>
  351. /// Reads system input to produce ShaderDebugPrintInput parameters.
  352. /// </summary>
  353. public static class ShaderDebugPrintInputProducer
  354. {
  355. /// <summary>
  356. /// Read system input.
  357. /// </summary>
  358. /// <returns>Input parameters for ShaderDebugPrintManager.</returns>
  359. static public ShaderDebugPrintInput Get()
  360. {
  361. var r = new ShaderDebugPrintInput();
  362. #if ENABLE_LEGACY_INPUT_MANAGER
  363. r.pos = Input.mousePosition;
  364. r.leftDown = Input.GetMouseButton(0);
  365. r.rightDown = Input.GetMouseButton(1);
  366. r.middleDown = Input.GetMouseButton(2);
  367. #endif
  368. #if ENABLE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM_PACKAGE
  369. // NOTE: needs Unity.InputSystem asmdef reference.
  370. var mouse = InputSystem.Mouse.current;
  371. r.pos = mouse.position.ReadValue();
  372. r.leftDown = mouse.leftButton.isPressed;
  373. r.rightDown = mouse.rightButton.isPressed;
  374. r.middleDown = mouse.middleButton.isPressed;
  375. #endif
  376. return r;
  377. }
  378. }
  379. }