Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

RenderGraphBuilders.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. using System;
  2. using System.Diagnostics;
  3. using UnityEngine.Experimental.Rendering;
  4. namespace UnityEngine.Rendering.RenderGraphModule
  5. {
  6. // This is a class making it a struct wouldn't help as we pas it around as an interface which means it would be boxed/unboxed anyway
  7. // Publicly this class has different faces to help the users with different pass types through type safety but internally
  8. // we just have a single implementation for all builders
  9. internal class RenderGraphBuilders : IBaseRenderGraphBuilder, IComputeRenderGraphBuilder, IRasterRenderGraphBuilder, IUnsafeRenderGraphBuilder
  10. {
  11. RenderGraphPass m_RenderPass;
  12. RenderGraphResourceRegistry m_Resources;
  13. RenderGraph m_RenderGraph;
  14. bool m_Disposed;
  15. public RenderGraphBuilders()
  16. {
  17. m_RenderPass = null;
  18. m_Resources = null;
  19. m_RenderGraph = null;
  20. m_Disposed = true;
  21. }
  22. public void Setup(RenderGraphPass renderPass, RenderGraphResourceRegistry resources, RenderGraph renderGraph)
  23. {
  24. #if DEVELOPMENT_BUILD || UNITY_EDITOR
  25. // If the object is not disposed yet this is an error as the pass is not finished (only in the dispose we register it with the rendergraph)
  26. // This is likely cause by a user not doing a clean using and then forgetting to manually dispose the object.
  27. if (m_Disposed != true)
  28. {
  29. throw new Exception("Please finish building the previous pass first by disposing the pass builder object before adding a new pass.");
  30. }
  31. #endif
  32. m_RenderPass = renderPass;
  33. m_Resources = resources;
  34. m_RenderGraph = renderGraph;
  35. m_Disposed = false;
  36. renderPass.useAllGlobalTextures = false;
  37. if (renderPass.type == RenderGraphPassType.Raster)
  38. {
  39. CommandBuffer.ThrowOnSetRenderTarget = true;
  40. }
  41. }
  42. public void EnableAsyncCompute(bool value)
  43. {
  44. m_RenderPass.EnableAsyncCompute(value);
  45. }
  46. public void AllowPassCulling(bool value)
  47. {
  48. m_RenderPass.AllowPassCulling(value);
  49. }
  50. public void AllowGlobalStateModification(bool value)
  51. {
  52. m_RenderPass.AllowGlobalState(value);
  53. }
  54. /// <summary>
  55. /// Enable foveated rendering for this pass.
  56. /// </summary>
  57. /// <param name="value">True to enable foveated rendering.</param>
  58. public void EnableFoveatedRasterization(bool value)
  59. {
  60. m_RenderPass.EnableFoveatedRasterization(value);
  61. }
  62. public BufferHandle CreateTransientBuffer(in BufferDesc desc)
  63. {
  64. var result = m_Resources.CreateBuffer(desc, m_RenderPass.index);
  65. UseResource(result.handle, AccessFlags.Write | AccessFlags.Read, isTransient: true);
  66. return result;
  67. }
  68. public BufferHandle CreateTransientBuffer(in BufferHandle computebuffer)
  69. {
  70. var desc = m_Resources.GetBufferResourceDesc(computebuffer.handle);
  71. return CreateTransientBuffer(desc);
  72. }
  73. public TextureHandle CreateTransientTexture(in TextureDesc desc)
  74. {
  75. var result = m_Resources.CreateTexture(desc, m_RenderPass.index);
  76. UseResource(result.handle, AccessFlags.Write | AccessFlags.Read, isTransient: true);
  77. return result;
  78. }
  79. public TextureHandle CreateTransientTexture(in TextureHandle texture)
  80. {
  81. var desc = m_Resources.GetTextureResourceDesc(texture.handle);
  82. return CreateTransientTexture(desc);
  83. }
  84. public void Dispose()
  85. {
  86. Dispose(true);
  87. }
  88. protected virtual void Dispose(bool disposing)
  89. {
  90. if (m_Disposed)
  91. return;
  92. try
  93. {
  94. if (disposing)
  95. {
  96. // Use all globals simply means this... we do a UseTexture on all globals so the pass has the correct dependencies.
  97. // This of course goes to show how bad an idea shader-system wide globals really are dependency/lifetime tracking wise :-)
  98. if (m_RenderPass.useAllGlobalTextures)
  99. {
  100. foreach (var texture in m_RenderGraph.AllGlobals())
  101. {
  102. this.UseTexture(texture, AccessFlags.Read);
  103. }
  104. }
  105. // Set globals on the graph fronted side so subsequent passes can have pass dependencies on these global texture handles
  106. foreach (var t in m_RenderPass.setGlobalsList)
  107. {
  108. m_RenderGraph.SetGlobal(t.Item1, t.Item2);
  109. }
  110. m_RenderGraph.OnPassAdded(m_RenderPass);
  111. }
  112. }
  113. finally
  114. {
  115. if (m_RenderPass.type == RenderGraphPassType.Raster)
  116. {
  117. CommandBuffer.ThrowOnSetRenderTarget = false;
  118. }
  119. m_RenderPass = null;
  120. m_Resources = null;
  121. m_RenderGraph = null;
  122. m_Disposed = true;
  123. }
  124. }
  125. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
  126. private void ValidateWriteTo(in ResourceHandle handle)
  127. {
  128. if (RenderGraph.enableValidityChecks)
  129. {
  130. // Write by design generates a new version of the resource. However
  131. // you could in theory write to v2 of the resource while there is already
  132. // a v3 so this write would then introduce a new v4 of the resource.
  133. // This would mean a divergence in the versioning history subsequent versions based on v2 and other subsequent versions based on v3
  134. // this would be very confusing as they are all still refered to by the same texture just different versions
  135. // so we decide to disallow this. It can always be (at zero cost) handled by using a "Move" pass to move the divergent version
  136. // so it's own texture resource which again has it's own single history of versions.
  137. if (handle.IsVersioned)
  138. {
  139. var name = m_Resources.GetRenderGraphResourceName(handle);
  140. throw new InvalidOperationException($"Trying to write to a versioned resource handle. You can only write to unversioned resource handles to avoid branches in the resource history. (pass {m_RenderPass.name} resource{name}).");
  141. }
  142. if (m_RenderPass.IsWritten(handle))
  143. {
  144. // We bump the version and write count if you call USeResource with writing flags. So calling this several times
  145. // would lead to the side effect of new versions for every call to UseResource leading to incorrect versions.
  146. // In theory we could detect and ignore the second UseResource but we decided to just disallow is as it might also
  147. // Stem from user confusion.
  148. // It seems the most likely cause of such a situation would be something like:
  149. // TextureHandle b = a;
  150. // ...
  151. // much much code in between, user lost track that a=b
  152. // ...
  153. // builder.WriteTexture(a)
  154. // builder.WriteTexture(b)
  155. // > Get this error they were probably thinking they were writing two separate outputs... but they are just two versions of resource 'a'
  156. // where they can only differ between by careful management of versioned resources.
  157. var name = m_Resources.GetRenderGraphResourceName(handle);
  158. throw new InvalidOperationException($"Trying to write a resource twice in a pass. You can only write the same resource once within a pass (pass {m_RenderPass.name} resource{name}).");
  159. }
  160. }
  161. }
  162. private ResourceHandle UseResource(in ResourceHandle handle, AccessFlags flags, bool isTransient = false)
  163. {
  164. CheckResource(handle);
  165. // If we are not discarding the resource, add a "read" dependency on the current version
  166. // this "Read" is a bit of a misnomer it really means more like "Preserve existing content or read"
  167. if ((flags & AccessFlags.Discard) == 0)
  168. {
  169. ResourceHandle versioned;
  170. if (!handle.IsVersioned)
  171. {
  172. versioned = m_Resources.GetLatestVersionHandle(handle);
  173. }
  174. else
  175. {
  176. versioned = handle;
  177. }
  178. if (isTransient)
  179. {
  180. m_RenderPass.AddTransientResource(versioned);
  181. return GetLatestVersionHandle(handle);
  182. }
  183. m_RenderPass.AddResourceRead(versioned);
  184. if ((flags & AccessFlags.Read) == 0)
  185. {
  186. // Flag the resource as being an "implicit read" so that we can distinguish it from a user-specified read
  187. m_RenderPass.implicitReadsList.Add(versioned);
  188. }
  189. }
  190. else
  191. {
  192. // We are discarding it but we still read it, so we add a dependency on version "0" of this resource
  193. if ((flags & AccessFlags.Read) != 0)
  194. {
  195. m_RenderPass.AddResourceRead(m_Resources.GetZeroVersionedHandle(handle));
  196. }
  197. }
  198. if ((flags & AccessFlags.Write) != 0)
  199. {
  200. ValidateWriteTo(handle);
  201. m_RenderPass.AddResourceWrite(m_Resources.GetNewVersionedHandle(handle));
  202. m_Resources.IncrementWriteCount(handle);
  203. }
  204. return GetLatestVersionHandle(handle);
  205. }
  206. public BufferHandle UseBuffer(in BufferHandle input, AccessFlags flags)
  207. {
  208. UseResource(input.handle, flags);
  209. return input;
  210. }
  211. // UseTexture and SetRenderAttachment are currently forced to be mutually exclusive in the same pass
  212. // check this.
  213. // We currently ignore the version. In theory there might be some cases that are actually allowed with versioning
  214. // for ample UseTexture(myTexV1, read) UseFragment(myTexV2, ReadWrite) as they are different versions
  215. // but for now we don't allow any of that.
  216. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
  217. private void CheckNotUseFragment(TextureHandle tex)
  218. {
  219. if(RenderGraph.enableValidityChecks)
  220. {
  221. bool usedAsFragment = false;
  222. usedAsFragment = (m_RenderPass.depthAccess.textureHandle.IsValid() && m_RenderPass.depthAccess.textureHandle.handle.index == tex.handle.index);
  223. if (!usedAsFragment)
  224. {
  225. for (int i = 0; i <= m_RenderPass.colorBufferMaxIndex; i++)
  226. {
  227. if (m_RenderPass.colorBufferAccess[i].textureHandle.IsValid() && m_RenderPass.colorBufferAccess[i].textureHandle.handle.index == tex.handle.index)
  228. {
  229. usedAsFragment = true;
  230. break;
  231. }
  232. }
  233. }
  234. if (usedAsFragment)
  235. {
  236. var name = m_Resources.GetRenderGraphResourceName(tex.handle);
  237. throw new ArgumentException($"Trying to UseTexture on a texture that is already used through SetRenderAttachment. Consider updating your code. (pass {m_RenderPass.name} resource{name}).");
  238. }
  239. }
  240. }
  241. public void UseTexture(in TextureHandle input, AccessFlags flags)
  242. {
  243. CheckNotUseFragment(input);
  244. UseResource(input.handle, flags);
  245. }
  246. public void UseGlobalTexture(int propertyId, AccessFlags flags)
  247. {
  248. var h = m_RenderGraph.GetGlobal(propertyId);
  249. if (h.IsValid())
  250. {
  251. UseTexture(h, flags);
  252. }
  253. else
  254. {
  255. throw new ArgumentException("Trying to read global texture property {globalPropertyID} but no previous pass in the graph assigned a value to this global.");
  256. }
  257. }
  258. public void UseAllGlobalTextures(bool enable)
  259. {
  260. m_RenderPass.useAllGlobalTextures = enable;
  261. }
  262. public void SetGlobalTextureAfterPass(in TextureHandle input, int propertyId)
  263. {
  264. m_RenderPass.setGlobalsList.Add(ValueTuple.Create(input, propertyId));
  265. }
  266. // Shared validation between SetRenderAttachment/SetRenderAttachmentDepth
  267. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
  268. private void CheckUseFragment(TextureHandle tex, bool isDepth)
  269. {
  270. if(RenderGraph.enableValidityChecks)
  271. {
  272. // We ignore the version as we don't allow mixing UseTexture/UseFragment between different versions
  273. // even though it should theoretically work (and we might do so in the future) for now we're overly strict.
  274. bool alreadyUsed = false;
  275. //TODO: Check grab textures here and allow if it's grabbed. For now
  276. // SetRenderAttachment()
  277. // UseTexture(grab)
  278. // will work but not the other way around
  279. for (int i = 0; i < m_RenderPass.resourceReadLists[tex.handle.iType].Count; i++)
  280. {
  281. if (m_RenderPass.resourceReadLists[tex.handle.iType][i].index == tex.handle.index)
  282. {
  283. alreadyUsed = true;
  284. break;
  285. }
  286. }
  287. for (int i = 0; i < m_RenderPass.resourceWriteLists[tex.handle.iType].Count; i++)
  288. {
  289. if (m_RenderPass.resourceWriteLists[tex.handle.iType][i].index == tex.handle.index)
  290. {
  291. alreadyUsed = true;
  292. break;
  293. }
  294. }
  295. if (alreadyUsed)
  296. {
  297. var name = m_Resources.GetRenderGraphResourceName(tex.handle);
  298. throw new InvalidOperationException($"Trying to SetRenderAttachment on a texture that is already used through UseTexture/SetRenderAttachment. Consider updating your code. (pass '{m_RenderPass.name}' resource '{name}').");
  299. }
  300. m_Resources.GetRenderTargetInfo(tex.handle, out var info);
  301. // The old path is full of invalid uses that somehow work (or seemt to work) so we skip the tests if not using actual native renderpass
  302. if (m_RenderGraph.nativeRenderPassesEnabled)
  303. {
  304. if (isDepth)
  305. {
  306. if (!GraphicsFormatUtility.IsDepthFormat(info.format))
  307. {
  308. var name = m_Resources.GetRenderGraphResourceName(tex.handle);
  309. throw new InvalidOperationException($"Trying to SetRenderAttachmentDepth on a texture that has a color format {info.format}. Use a texture with a depth format instead. (pass '{m_RenderPass.name}' resource '{name}').");
  310. }
  311. }
  312. else
  313. {
  314. if (GraphicsFormatUtility.IsDepthFormat(info.format))
  315. {
  316. var name = m_Resources.GetRenderGraphResourceName(tex.handle);
  317. throw new InvalidOperationException($"Trying to SetRenderAttachment on a texture that has a depth format. Use a texture with a color format instead. (pass '{m_RenderPass.name}' resource '{name}').");
  318. }
  319. }
  320. }
  321. foreach (var globalTex in m_RenderPass.setGlobalsList)
  322. {
  323. if (globalTex.Item1.handle.index == tex.handle.index)
  324. {
  325. throw new InvalidOperationException($"Trying to SetRenderAttachment on a texture that is currently set on a global texture slot. Shaders might be using the texture using samplers. You should ensure textures are not set as globals when using them as fragment attachments.");
  326. }
  327. }
  328. }
  329. }
  330. public void SetRenderAttachment(TextureHandle tex, int index, AccessFlags flags, int mipLevel, int depthSlice)
  331. {
  332. CheckUseFragment(tex, false);
  333. ResourceHandle result = UseResource(tex.handle, flags);
  334. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  335. // it doesn't really matter as it's really the Read/Write lists that determine that
  336. // This is just to keep track of the handle->mrt index mapping
  337. var th = new TextureHandle();
  338. th.handle = result;
  339. m_RenderPass.SetColorBufferRaw(th, index, flags, mipLevel, depthSlice);
  340. }
  341. public void SetInputAttachment(TextureHandle tex, int index, AccessFlags flags, int mipLevel, int depthSlice)
  342. {
  343. CheckUseFragment(tex, false);
  344. ResourceHandle result = UseResource(tex.handle, flags);
  345. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  346. // it doesn't really matter as it's really the Read/Write lists that determine that
  347. // This is just to keep track of the handle->mrt index mapping
  348. var th = new TextureHandle();
  349. th.handle = result;
  350. m_RenderPass.SetFragmentInputRaw(th, index, flags, mipLevel, depthSlice);
  351. }
  352. public void SetRenderAttachmentDepth(TextureHandle tex, AccessFlags flags, int mipLevel, int depthSlice)
  353. {
  354. CheckUseFragment(tex, true);
  355. ResourceHandle result = UseResource(tex.handle, flags);
  356. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  357. // it doesn't really matter as it's really the Read/Write lists that determine that
  358. // This is just to keep track to bind this handle as a depth texture.
  359. var th = new TextureHandle();
  360. th.handle = result;
  361. m_RenderPass.SetDepthBufferRaw(th, flags, mipLevel, depthSlice);
  362. }
  363. public TextureHandle SetRandomAccessAttachment(TextureHandle input, int index, AccessFlags flags = AccessFlags.Read)
  364. {
  365. CheckNotUseFragment(input);
  366. ResourceHandle result = UseResource(input.handle, flags);
  367. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  368. // it doesn't really matter as it's really the Read/Write lists that determine that
  369. // This is just to keep track of the resources to bind before execution
  370. var th = new TextureHandle();
  371. th.handle = result;
  372. m_RenderPass.SetRandomWriteResourceRaw(th.handle, index, false, flags);
  373. return input;
  374. }
  375. public BufferHandle UseBufferRandomAccess(BufferHandle input, int index, AccessFlags flags = AccessFlags.Read)
  376. {
  377. var h = UseBuffer(input, flags);
  378. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  379. // it doesn't really matter as it's really the Read/Write lists that determine that
  380. // This is just to keep track of the resources to bind before execution
  381. m_RenderPass.SetRandomWriteResourceRaw(h.handle, index, true, flags);
  382. return input;
  383. }
  384. public BufferHandle UseBufferRandomAccess(BufferHandle input, int index, bool preserveCounterValue, AccessFlags flags = AccessFlags.Read)
  385. {
  386. var h = UseBuffer(input, flags);
  387. // Note the version for the attachments is a bit arbitrary so we just use the latest for now
  388. // it doesn't really matter as it's really the Read/Write lists that determine that
  389. // This is just to keep track of the resources to bind before execution
  390. m_RenderPass.SetRandomWriteResourceRaw(h.handle, index, preserveCounterValue, flags);
  391. return input;
  392. }
  393. public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, ComputeGraphContext> renderFunc) where PassData : class, new()
  394. {
  395. ((ComputeRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
  396. }
  397. public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, RasterGraphContext> renderFunc) where PassData : class, new()
  398. {
  399. ((RasterRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
  400. }
  401. public void SetRenderFunc<PassData>(BaseRenderFunc<PassData, UnsafeGraphContext> renderFunc) where PassData : class, new()
  402. {
  403. ((UnsafeRenderGraphPass<PassData>)m_RenderPass).renderFunc = renderFunc;
  404. }
  405. public void UseRendererList(in RendererListHandle input)
  406. {
  407. m_RenderPass.UseRendererList(input);
  408. }
  409. private ResourceHandle GetLatestVersionHandle(in ResourceHandle handle)
  410. {
  411. // Transient resources can't be used outside the pass so can never be versioned
  412. if (m_Resources.GetRenderGraphResourceTransientIndex(handle) >= 0)
  413. {
  414. return handle;
  415. }
  416. return m_Resources.GetLatestVersionHandle(handle);
  417. }
  418. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
  419. void CheckResource(in ResourceHandle res, bool checkTransientReadWrite = false)
  420. {
  421. if(RenderGraph.enableValidityChecks)
  422. {
  423. if (res.IsValid())
  424. {
  425. int transientIndex = m_Resources.GetRenderGraphResourceTransientIndex(res);
  426. // We have dontCheckTransientReadWrite here because users may want to use UseColorBuffer/UseDepthBuffer API to benefit from render target auto binding. In this case we don't want to raise the error.
  427. if (transientIndex == m_RenderPass.index && checkTransientReadWrite)
  428. {
  429. Debug.LogError($"Trying to read or write a transient resource at pass {m_RenderPass.name}.Transient resource are always assumed to be both read and written.");
  430. }
  431. if (transientIndex != -1 && transientIndex != m_RenderPass.index)
  432. {
  433. throw new ArgumentException($"Trying to use a transient {res.type} (pass index {transientIndex}) in a different pass (pass index {m_RenderPass.index}).");
  434. }
  435. }
  436. else
  437. {
  438. throw new ArgumentException($"Trying to use an invalid resource (pass {m_RenderPass.name}).");
  439. }
  440. }
  441. }
  442. }
  443. }