A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Diagnostics; 4using System.Runtime.CompilerServices; 5using UnityEngine.Experimental.Rendering; 6using UnityEngine.Scripting.APIUpdating; 7// Typedef for the in-engine RendererList API (to avoid conflicts with the experimental version) 8using CoreRendererListDesc = UnityEngine.Rendering.RendererUtils.RendererListDesc; 9 10namespace UnityEngine.Rendering.RenderGraphModule 11{ 12 /// <summary> 13 /// Sets the read and write access for the depth buffer. 14 /// </summary> 15 [Flags][MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 16 public enum DepthAccess 17 { 18 ///<summary>Read Access.</summary> 19 Read = 1 << 0, 20 ///<summary>Write Access.</summary> 21 Write = 1 << 1, 22 ///<summary>Read and Write Access.</summary> 23 ReadWrite = Read | Write, 24 } 25 26 /// <summary> 27 /// Express the operations the rendergraph pass will do on a resource. 28 /// </summary> 29 [Flags][MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 30 public enum AccessFlags 31 { 32 ///<summary>The pass does not access the resource at all. Calling Use* functions with none has no effect.</summary> 33 None = 0, 34 35 ///<summary>This pass will read data the resource. Data in the resource should never be written unless one of the write flags is also present. Writing to a read-only resource may lead to undefined results, significant performance penaties, and GPU crashes.</summary> 36 Read = 1 << 0, 37 38 ///<summary>This pass will at least write some data to the resource. Data in the resource should never be read unless one of the read flags is also present. Reading from a write-only resource may lead to undefined results, significant performance penaties, and GPU crashes.</summary> 39 Write = 1 << 1, 40 41 ///<summary>Previous data in the resource is not preserved. The resource will contain undefined data at the beginning of the pass.</summary> 42 Discard = 1 << 2, 43 44 ///<summary>All data in the resource will be written by this pass. Data in the resource should never be read.</summary> 45 WriteAll = Write | Discard, 46 47 ///<summary> Shortcut for Read | Write</summary> 48 ReadWrite = Read | Write 49 } 50 51 /// <summary> 52 /// An object representing the internal context of a rendergraph pass execution. 53 /// This object is public for technical reasons only and should not be used. 54 /// </summary> 55 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 56 public class InternalRenderGraphContext 57 { 58 internal ScriptableRenderContext renderContext; 59 internal CommandBuffer cmd; 60 internal RenderGraphObjectPool renderGraphPool; 61 internal RenderGraphDefaultResources defaultResources; 62 internal RenderGraphPass executingPass; 63 internal bool contextlessTesting; 64 } 65 66 // This whole thing is a bit of a mess InternalRenderGraphContext is public (but all members are internal) 67 // just because the C# standard says that all interface member function implementations should be public. 68 // So below in for example the RasterGraphContext we can't implement the (internal) interface as 69 // internal void FromInternalContext(InternalRenderGraphContext context) { ... } 70 // So we have to make FromInternalContext public so InternalRenderGraphContext also becomes public. 71 // This seems an oversight in c# where Interfaces used as Generic constraints could very well be useful 72 // with internal only functions. 73 74 internal interface IDerivedRendergraphContext 75 { 76 /// <summary> 77 /// This function is only public for techical resons of the c# language and should not be called outside the package. 78 /// </summary> 79 /// <param name="context">The context to convert</param> 80 public void FromInternalContext(InternalRenderGraphContext context); 81 } 82 83 /// <summary> 84 /// This class specifies the context given to every render pass. This context type passes a generic 85 /// command buffer that can be used to schedule all commands. This will eventually be deprecated 86 /// in favor of more specific contexts that have more specific command buffer types. 87 /// </summary> 88 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 89 public struct RenderGraphContext : IDerivedRendergraphContext 90 { 91 private InternalRenderGraphContext wrappedContext; 92 93 /// <inheritdoc /> 94 public void FromInternalContext(InternalRenderGraphContext context) 95 { 96 wrappedContext = context; 97 } 98 99 ///<summary>Scriptable Render Context used for rendering.</summary> 100 public ScriptableRenderContext renderContext { get => wrappedContext.renderContext; } 101 ///<summary>Command Buffer used for rendering.</summary> 102 public CommandBuffer cmd { get => wrappedContext.cmd; } 103 ///<summary>Render Graph pool used for temporary data.</summary> 104 public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; } 105 ///<summary>Render Graph default resources.</summary> 106 public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; } 107 } 108 109 /// <summary> 110 /// This class declares the context object passed to the execute function of a raster render pass. 111 /// <see cref="RenderGraph.AddRasterRenderPass"/> 112 /// </summary> 113 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 114 public struct RasterGraphContext : IDerivedRendergraphContext 115 { 116 private InternalRenderGraphContext wrappedContext; 117 118 ///<summary>Command Buffer used for rendering.</summary> 119 public RasterCommandBuffer cmd; 120 121 ///<summary>Render Graph default resources.</summary> 122 public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; } 123 124 ///<summary>Render Graph pool used for temporary data.</summary> 125 public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; } 126 127 static internal RasterCommandBuffer rastercmd = new RasterCommandBuffer(null, null, false); 128 /// <inheritdoc /> 129 public void FromInternalContext(InternalRenderGraphContext context) 130 { 131 wrappedContext = context; 132 rastercmd.m_WrappedCommandBuffer = wrappedContext.cmd; 133 rastercmd.m_ExecutingPass = context.executingPass; 134 cmd = rastercmd; 135 } 136 } 137 138 /// <summary> 139 /// This class declares the context object passed to the execute function of a compute render pass. 140 /// <see cref="RenderGraph.AddComputePass"/> 141 /// </summary> 142 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 143 public class ComputeGraphContext : IDerivedRendergraphContext 144 { 145 private InternalRenderGraphContext wrappedContext; 146 147 ///<summary>Command Buffer used for rendering.</summary> 148 public ComputeCommandBuffer cmd; 149 150 ///<summary>Render Graph default resources.</summary> 151 public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; } 152 153 ///<summary>Render Graph pool used for temporary data.</summary> 154 public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; } 155 156 static internal ComputeCommandBuffer computecmd = new ComputeCommandBuffer(null, null, false); 157 158 /// <inheritdoc /> 159 public void FromInternalContext(InternalRenderGraphContext context) 160 { 161 wrappedContext = context; 162 computecmd.m_WrappedCommandBuffer = wrappedContext.cmd; 163 computecmd.m_ExecutingPass = context.executingPass; 164 cmd = computecmd; 165 } 166 } 167 168 /// <summary> 169 /// This class declares the context object passed to the execute function of an unsafe render pass. 170 /// <see cref="RenderGraph.AddUnsafePass"/> 171 /// </summary> 172 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 173 public class UnsafeGraphContext : IDerivedRendergraphContext 174 { 175 private InternalRenderGraphContext wrappedContext; 176 177 ///<summary>Unsafe Command Buffer used for rendering.</summary> 178 public UnsafeCommandBuffer cmd; 179 180 ///<summary>Render Graph default resources.</summary> 181 public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; } 182 183 ///<summary>Render Graph pool used for temporary data.</summary> 184 public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; } 185 186 internal static UnsafeCommandBuffer unsCmd = new UnsafeCommandBuffer(null, null, false); 187 /// <inheritdoc /> 188 public void FromInternalContext(InternalRenderGraphContext context) 189 { 190 wrappedContext = context; 191 unsCmd.m_WrappedCommandBuffer = wrappedContext.cmd; 192 unsCmd.m_ExecutingPass = context.executingPass; 193 cmd = unsCmd; 194 } 195 } 196 197 /// <summary> 198 /// This struct contains properties which control the execution of the Render Graph. 199 /// </summary> 200 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 201 public struct RenderGraphParameters 202 { 203 ///<summary>Identifier for this render graph execution.</summary> 204 public string executionName; 205 ///<summary>Index of the current frame being rendered.</summary> 206 public int currentFrameIndex; 207 ///<summary> Controls whether to enable Renderer List culling or not.</summary> 208 public bool rendererListCulling; 209 ///<summary>Scriptable Render Context used by the render pipeline.</summary> 210 public ScriptableRenderContext scriptableRenderContext; 211 ///<summary>Command Buffer used to execute graphic commands.</summary> 212 public CommandBuffer commandBuffer; 213 ///<summary>When running tests indicate the context is intentionally invalid and all calls on it should just do nothing. 214 ///This allows you to run tests that rely on code execution the way to the pass render functions 215 ///This also changes some behaviours with exception handling and error logging so the test framework can act on exceptions to validate behaviour better.</summary> 216 internal bool invalidContextForTesting; 217 } 218 219 /// <summary> 220 /// The Render Pass rendering delegate to use with typed contexts. 221 /// </summary> 222 /// <typeparam name="PassData">The type of the class used to provide data to the Render Pass.</typeparam> 223 /// <typeparam name="ContextType">The type of the context that will be passed to the render function.</typeparam> 224 /// <param name="data">Render Pass specific data.</param> 225 /// <param name="renderGraphContext">Global Render Graph context.</param> 226 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 227 public delegate void BaseRenderFunc<PassData, ContextType>(PassData data, ContextType renderGraphContext) where PassData : class, new(); 228 229 230 /// <summary> 231 /// This class is the main entry point of the Render Graph system. 232 /// </summary> 233 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 234 public partial class RenderGraph 235 { 236 ///<summary>Maximum number of MRTs supported by Render Graph.</summary> 237 public static readonly int kMaxMRTCount = 8; 238 239 internal struct CompiledResourceInfo 240 { 241 public List<int> producers; 242 public List<int> consumers; 243 public int refCount; 244 public bool imported; 245 246 public void Reset() 247 { 248 if (producers == null) 249 producers = new List<int>(); 250 if (consumers == null) 251 consumers = new List<int>(); 252 253 producers.Clear(); 254 consumers.Clear(); 255 refCount = 0; 256 imported = false; 257 } 258 } 259 260 [DebuggerDisplay("RenderPass: {name} (Index:{index} Async:{enableAsyncCompute})")] 261 internal struct CompiledPassInfo 262 { 263 public string name; 264 public int index; 265 266 public List<int>[] resourceCreateList; 267 public List<int>[] resourceReleaseList; 268 public GraphicsFence fence; 269 270#if DEVELOPMENT_BUILD || UNITY_EDITOR 271 // This members are only here to ease debugging. 272 public List<string>[] debugResourceReads; 273 public List<string>[] debugResourceWrites; 274#endif 275 276 public int refCount; 277 public int syncToPassIndex; // Index of the pass that needs to be waited for. 278 public int syncFromPassIndex; // Smaller pass index that waits for this pass. 279 280 public bool enableAsyncCompute; 281 public bool allowPassCulling; 282 public bool needGraphicsFence; 283 public bool culled; 284 public bool culledByRendererList; 285 public bool hasSideEffect; 286 public bool enableFoveatedRasterization; 287 288 public void Reset(RenderGraphPass pass, int index) 289 { 290 name = pass.name; 291 this.index = index; 292 293 enableAsyncCompute = pass.enableAsyncCompute; 294 allowPassCulling = pass.allowPassCulling; 295 enableFoveatedRasterization = pass.enableFoveatedRasterization; 296 297 if (resourceCreateList == null) 298 { 299 resourceCreateList = new List<int>[(int)RenderGraphResourceType.Count]; 300 resourceReleaseList = new List<int>[(int)RenderGraphResourceType.Count]; 301 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 302 { 303 resourceCreateList[i] = new List<int>(); 304 resourceReleaseList[i] = new List<int>(); 305 } 306 307#if DEVELOPMENT_BUILD || UNITY_EDITOR 308 debugResourceReads = new List<string>[(int)RenderGraphResourceType.Count]; 309 debugResourceWrites = new List<string>[(int)RenderGraphResourceType.Count]; 310 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 311 { 312 debugResourceReads[i] = new List<string>(); 313 debugResourceWrites[i] = new List<string>(); 314 } 315#endif 316 } 317 318 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 319 { 320 resourceCreateList[i].Clear(); 321 resourceReleaseList[i].Clear(); 322 } 323 324 refCount = 0; 325 culled = false; 326 culledByRendererList = false; 327 hasSideEffect = false; 328 syncToPassIndex = -1; 329 syncFromPassIndex = -1; 330 needGraphicsFence = false; 331 332#if DEVELOPMENT_BUILD || UNITY_EDITOR 333 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 334 { 335 debugResourceReads[i].Clear(); 336 debugResourceWrites[i].Clear(); 337 } 338#endif 339 } 340 } 341 342 /// <summary> 343 /// Enable the use of the render pass API by the graph instead of traditional SetRenderTarget. This is an advanced 344 /// feature and users have to be aware of the specific impact it has on rendergraph/graphics APIs below. 345 /// 346 /// When enabled, the render graph try to use render passes and supasses instead of relying on SetRendertarget. It 347 /// will try to aggressively optimize the number of BeginRenderPass+EndRenderPass calls as well as calls to NextSubPass. 348 /// This with the aim to maximize the time spent "on chip" on tile based renderers. 349 /// 350 /// The Graph will automatically determine when to break render passes as well as the load and store actions to apply to these render passes. 351 /// To do this, the graph will analyze the use of textures. E.g. when a texture is used twice in a row as a active render target, the two 352 /// render graph passes will be merged in a single render pass with two surpasses. On the other hand if a render target is sampled as a texture in 353 /// a later pass this render target will be stored (and possibly resolved) and the render pass will be broken up. 354 /// 355 /// When setting this setting to true some existing render graph API is no longer valid as it can't express detailed frame information needed to emit 356 /// native render pases. In particular: 357 /// - The ImportBackbuffer overload without a RenderTargetInfo argument. 358 /// - Any AddRenderPass overloads. The more specific AddRasterRenderPass/AddComputePass/AddUnsafePass functions should be used to register passes. 359 /// 360 /// In addition to this, additional validation will be done on the correctness of arguments of existing API that was not previously done. This could lead 361 /// to new errors when using existing render graph code with nativeRenderPassesEnabled. 362 /// 363 /// Note: that CommandBuffer.BeginRenderPass/EndRenderPass calls are different by design from SetRenderTarget so this could also have 364 /// effects outside of render graph (e.g. for code relying on the currently active render target as this will not be updated when using render passes). 365 /// </summary> 366 public bool nativeRenderPassesEnabled 367 { 368 get; set; 369 } 370 371 internal/*for tests*/ RenderGraphResourceRegistry m_Resources; 372 RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); 373 RenderGraphBuilders m_builderInstance = new RenderGraphBuilders(); 374 internal/*for tests*/ List<RenderGraphPass> m_RenderPasses = new List<RenderGraphPass>(64); 375 List<RendererListHandle> m_RendererLists = new List<RendererListHandle>(32); 376 RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams(); 377 RenderGraphLogger m_FrameInformationLogger = new RenderGraphLogger(); 378 RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources(); 379 Dictionary<int, ProfilingSampler> m_DefaultProfilingSamplers = new Dictionary<int, ProfilingSampler>(); 380 InternalRenderGraphContext m_RenderGraphContext = new InternalRenderGraphContext(); 381 CommandBuffer m_PreviousCommandBuffer; 382 List<int>[] m_ImmediateModeResourceList = new List<int>[(int)RenderGraphResourceType.Count]; 383 RenderGraphCompilationCache m_CompilationCache; 384 385 RenderTargetIdentifier[][] m_TempMRTArrays = null; 386 387 internal interface ICompiledGraph 388 { 389 public void Clear(); 390 } 391 392 // Compiled Render Graph info. 393 internal class CompiledGraph : ICompiledGraph 394 { 395 // This is a 1:1 mapping on the resource handle indexes, this means the first element with index 0 will represent the "null" handle 396 public DynamicArray<CompiledResourceInfo>[] compiledResourcesInfos = new DynamicArray<CompiledResourceInfo>[(int)RenderGraphResourceType.Count]; 397 public DynamicArray<CompiledPassInfo> compiledPassInfos = new DynamicArray<CompiledPassInfo>(); 398 public int lastExecutionFrame; 399 400 public CompiledGraph() 401 { 402 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 403 { 404 compiledResourcesInfos[i] = new DynamicArray<CompiledResourceInfo>(); 405 } 406 } 407 408 public void Clear() 409 { 410 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 411 compiledResourcesInfos[i].Clear(); 412 compiledPassInfos.Clear(); 413 } 414 415 void InitResourceInfosData(DynamicArray<CompiledResourceInfo> resourceInfos, int count) 416 { 417 resourceInfos.Resize(count); 418 for (int i = 0; i < resourceInfos.size; ++i) 419 resourceInfos[i].Reset(); 420 } 421 422 public void InitializeCompilationData(List<RenderGraphPass> passes, RenderGraphResourceRegistry resources) 423 { 424 InitResourceInfosData(compiledResourcesInfos[(int)RenderGraphResourceType.Texture], resources.GetTextureResourceCount()); 425 InitResourceInfosData(compiledResourcesInfos[(int)RenderGraphResourceType.Buffer], resources.GetBufferResourceCount()); 426 InitResourceInfosData(compiledResourcesInfos[(int)RenderGraphResourceType.AccelerationStructure], resources.GetRayTracingAccelerationStructureResourceCount()); 427 428 compiledPassInfos.Resize(passes.Count); 429 for (int i = 0; i < compiledPassInfos.size; ++i) 430 compiledPassInfos[i].Reset(passes[i], i); 431 } 432 } 433 434 Stack<int> m_CullingStack = new Stack<int>(); 435 436 string m_CurrentExecutionName; 437 int m_ExecutionCount; 438 int m_CurrentFrameIndex; 439 int m_CurrentImmediatePassIndex; 440 bool m_ExecutionExceptionWasRaised; 441 bool m_HasRenderGraphBegun; 442 bool m_RendererListCulling; 443 bool m_EnableCompilationCaching; 444 CompiledGraph m_DefaultCompiledGraph = new(); 445 CompiledGraph m_CurrentCompiledGraph; 446 string m_CaptureDebugDataForExecution; // Null unless debug data has been requested 447 448 Dictionary<string, DebugData> m_DebugData = new Dictionary<string, DebugData>(); 449 450 // Global list of living render graphs 451 static List<RenderGraph> s_RegisteredGraphs = new List<RenderGraph>(); 452 453 #region Public Interface 454 /// <summary>Name of the Render Graph.</summary> 455 public string name { get; private set; } = "RenderGraph"; 456 457 /// <summary>Request debug data be captured for the provided execution on the next frame.</summary> 458 internal void RequestCaptureDebugData(string executionName) 459 { 460 m_CaptureDebugDataForExecution = executionName; 461 } 462 463 /// <summary>If true, the Render Graph Viewer is active.</summary> 464 public static bool isRenderGraphViewerActive { get; internal set; } 465 466 /// <summary>If true, the Render Graph will run its various validity checks while processing (not considered in release mode).</summary> 467 internal static bool enableValidityChecks { get; private set; } 468 469 /// <summary> 470 /// Set of default resources usable in a pass rendering code. 471 /// </summary> 472 public RenderGraphDefaultResources defaultResources 473 { 474 get 475 { 476 return m_DefaultResources; 477 } 478 } 479 480 /// <summary> 481 /// Render Graph constructor. 482 /// </summary> 483 /// <param name="name">Optional name used to identify the render graph instnace.</param> 484 public RenderGraph(string name = "RenderGraph") 485 { 486 this.name = name; 487 if (GraphicsSettings.TryGetRenderPipelineSettings<RenderGraphGlobalSettings>(out var renderGraphGlobalSettings)) 488 { 489 m_EnableCompilationCaching = renderGraphGlobalSettings.enableCompilationCaching; 490 if (m_EnableCompilationCaching) 491 m_CompilationCache = new RenderGraphCompilationCache(); 492 493 enableValidityChecks = renderGraphGlobalSettings.enableValidityChecks; 494 } 495 else // No SRP pipeline is present/active, it can happen with unit tests 496 { 497 enableValidityChecks = true; 498 } 499 500 m_TempMRTArrays = new RenderTargetIdentifier[kMaxMRTCount][]; 501 for (int i = 0; i < kMaxMRTCount; ++i) 502 m_TempMRTArrays[i] = new RenderTargetIdentifier[i + 1]; 503 504 m_Resources = new RenderGraphResourceRegistry(m_DebugParameters, m_FrameInformationLogger); 505 s_RegisteredGraphs.Add(this); 506 onGraphRegistered?.Invoke(this); 507 } 508 509 /// <summary> 510 /// Cleanup the Render Graph. 511 /// </summary> 512 public void Cleanup() 513 { 514 m_Resources.Cleanup(); 515 m_DefaultResources.Cleanup(); 516 m_RenderGraphPool.Cleanup(); 517 518 s_RegisteredGraphs.Remove(this); 519 onGraphUnregistered?.Invoke(this); 520 521 nativeCompiler?.contextData?.Dispose(); 522 523 m_CompilationCache?.Clear(); 524 } 525 526 internal RenderGraphDebugParams debugParams => m_DebugParameters; 527 528 internal List<DebugUI.Widget> GetWidgetList() 529 { 530 return m_DebugParameters.GetWidgetList(name); 531 } 532 533 internal bool areAnySettingsActive => m_DebugParameters.AreAnySettingsActive; 534 535 /// <summary> 536 /// Register the render graph to the debug window. 537 /// </summary> 538 /// <param name="panel">Optional debug panel to which the render graph debug parameters will be registered.</param> 539 public void RegisterDebug(DebugUI.Panel panel = null) 540 { 541 m_DebugParameters.RegisterDebug(name, panel); 542 } 543 544 /// <summary> 545 /// Unregister render graph from the debug window. 546 /// </summary> 547 public void UnRegisterDebug() 548 { 549 m_DebugParameters.UnRegisterDebug(this.name); 550 } 551 552 /// <summary> 553 /// Get the list of all registered render graphs. 554 /// </summary> 555 /// <returns>The list of all registered render graphs.</returns> 556 public static List<RenderGraph> GetRegisteredRenderGraphs() 557 { 558 return s_RegisteredGraphs; 559 } 560 561 /// <summary> 562 /// Returns the last rendered frame debug data. Can be null if requireDebugData is set to false. 563 /// </summary> 564 /// <returns>The last rendered frame debug data</returns> 565 internal DebugData GetDebugData(string executionName) 566 { 567 if (m_DebugData.TryGetValue(executionName, out var debugData)) 568 return debugData; 569 570 return null; 571 } 572 573 /// <summary> 574 /// End frame processing. Purge resources that have been used since last frame and resets internal states. 575 /// This need to be called once per frame. 576 /// </summary> 577 public void EndFrame() 578 { 579 m_Resources.PurgeUnusedGraphicsResources(); 580 581 if (m_DebugParameters.logFrameInformation) 582 { 583 Debug.Log(m_FrameInformationLogger.GetAllLogs()); 584 m_DebugParameters.logFrameInformation = false; 585 } 586 if (m_DebugParameters.logResources) 587 { 588 m_Resources.FlushLogs(); 589 m_DebugParameters.logResources = false; 590 } 591 } 592 593 /// <summary> 594 /// Import an external texture to the Render Graph. 595 /// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled. 596 /// </summary> 597 /// <param name="rt">External RTHandle that needs to be imported.</param> 598 /// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns> 599 public TextureHandle ImportTexture(RTHandle rt) 600 { 601 return m_Resources.ImportTexture(rt); 602 } 603 604 /// <summary> 605 /// Import an external texture to the Render Graph. 606 /// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled. 607 /// 608 /// Note: RTHandles that wrap RenderTargetIdentifier will fail to import using this overload as render graph can't derive the render texture's properties. 609 /// In that case the overload taking a RenderTargetInfo argument should be used instead. 610 /// </summary> 611 /// <param name="rt">External RTHandle that needs to be imported.</param> 612 /// <param name="importParams">Info describing the clear behavior of imported textures. Clearing textures using importParams may be more efficient than manually clearing the texture using `cmd.Clear` on some hardware.</param> 613 /// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns> 614 public TextureHandle ImportTexture(RTHandle rt, ImportResourceParams importParams ) 615 { 616 return m_Resources.ImportTexture(rt, importParams); 617 } 618 619 620 /// <summary> 621 /// Import an external texture to the Render Graph. This overload should be used for RTHandles wrapping a RenderTargetIdentifier. 622 /// If the RTHandle is wrapping a RenderTargetIdentifer, Rendergrpah can't derive the render texture's properties so the user has to provide this info to the graph through RenderTargetInfo. 623 /// 624 /// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled. 625 /// 626 /// Note: To avoid inconsistencies between the passed in RenderTargetInfo and render texture this overload can only be used when the RTHandle is wrapping a RenderTargetIdentifier. 627 /// If this is not the case, the overload of ImportTexture without a RenderTargetInfo argument should be used instead. 628 /// </summary> 629 /// <param name="rt">External RTHandle that needs to be imported.</param> 630 /// <param name="info">The properties of the passed in RTHandle.</param> 631 /// <param name="importParams">Info describing the clear behavior of imported textures. Clearing textures using importParams may be more efficient than manually clearing the texture using `cmd.Clear` on some hardware.</param> 632 /// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns> 633 public TextureHandle ImportTexture(RTHandle rt, RenderTargetInfo info, ImportResourceParams importParams = new ImportResourceParams() ) 634 { 635 return m_Resources.ImportTexture(rt, info, importParams); 636 } 637 638 /// <summary> 639 /// Import an external texture to the Render Graph and set the handle as builtin handle. This can only happen from within the graph module 640 /// so it is internal. 641 /// </summary> 642 internal TextureHandle ImportTexture(RTHandle rt, bool isBuiltin) 643 { 644 return m_Resources.ImportTexture(rt, isBuiltin); 645 } 646 647 /// <summary> 648 /// Import the final backbuffer to render graph. The rendergraph can't derive the properties of a RenderTargetIdentifier as it is an opaque handle so the user has to pass them in through the info argument. 649 /// </summary> 650 /// <param name="rt">Backbuffer render target identifier.</param> 651 /// <param name="info">The properties of the passed in RTHandle.</param> 652 /// <param name="importParams">Info describing the clear behavior of imported textures. Clearing textures using importParams may be more efficient than manually clearing the texture using `cmd.Clear` on some hardware.</param> 653 /// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns> 654 public TextureHandle ImportBackbuffer(RenderTargetIdentifier rt, RenderTargetInfo info, ImportResourceParams importParams = new ImportResourceParams()) 655 { 656 return m_Resources.ImportBackbuffer(rt, info, importParams); 657 } 658 659 /// <summary> 660 /// Import the final backbuffer to render graph. 661 /// This function can only be used when nativeRenderPassesEnabled is false. 662 /// </summary> 663 /// <param name="rt">Backbuffer render target identifier.</param> 664 /// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns> 665 public TextureHandle ImportBackbuffer(RenderTargetIdentifier rt) 666 { 667 RenderTargetInfo dummy = new RenderTargetInfo(); 668 dummy.width = dummy.height = dummy.volumeDepth = dummy.msaaSamples = 1; 669 dummy.format = GraphicsFormat.R8G8B8A8_SRGB; 670 return m_Resources.ImportBackbuffer(rt, dummy, new ImportResourceParams()); 671 } 672 673 /// <summary> 674 /// Create a new Render Graph Texture resource. 675 /// </summary> 676 /// <param name="desc">Texture descriptor.</param> 677 /// <returns>A new TextureHandle.</returns> 678 public TextureHandle CreateTexture(in TextureDesc desc) 679 { 680 return m_Resources.CreateTexture(desc); 681 } 682 683 /// <summary> 684 /// Create a new Render Graph Shared Texture resource. 685 /// This texture will be persistent across render graph executions. 686 /// </summary> 687 /// <param name="desc">Creation descriptor of the texture.</param> 688 /// <param name="explicitRelease">Set to true if you want to manage the lifetime of the resource yourself. Otherwise the resource will be released automatically if unused for a time.</param> 689 /// <returns>A new TextureHandle.</returns> 690 public TextureHandle CreateSharedTexture(in TextureDesc desc, bool explicitRelease = false) 691 { 692 if (m_HasRenderGraphBegun) 693 throw new InvalidOperationException("A shared texture can only be created outside of render graph execution."); 694 695 return m_Resources.CreateSharedTexture(desc, explicitRelease); 696 } 697 698 /// <summary> 699 /// Refresh a shared texture with a new descriptor. 700 /// </summary> 701 /// <param name="handle">Shared texture that needs to be updated.</param> 702 /// <param name="desc">New Descriptor for the texture.</param> 703 public void RefreshSharedTextureDesc(TextureHandle handle, in TextureDesc desc) 704 { 705 m_Resources.RefreshSharedTextureDesc(handle, desc); 706 } 707 708 /// <summary> 709 /// Release a Render Graph shared texture resource. 710 /// </summary> 711 /// <param name="texture">The handle to the texture that needs to be release.</param> 712 public void ReleaseSharedTexture(TextureHandle texture) 713 { 714 if (m_HasRenderGraphBegun) 715 throw new InvalidOperationException("A shared texture can only be release outside of render graph execution."); 716 717 m_Resources.ReleaseSharedTexture(texture); 718 } 719 720 /// <summary> 721 /// Create a new Render Graph Texture resource using the descriptor from another texture. 722 /// </summary> 723 /// <param name="texture">Texture from which the descriptor should be used.</param> 724 /// <returns>A new TextureHandle.</returns> 725 public TextureHandle CreateTexture(TextureHandle texture) 726 { 727 return m_Resources.CreateTexture(m_Resources.GetTextureResourceDesc(texture.handle)); 728 } 729 730 /// <summary> 731 /// Create a new Render Graph Texture if the passed handle is invalid and use said handle as output. 732 /// If the passed handle is valid, no texture is created. 733 /// </summary> 734 /// <param name="desc">Desc used to create the texture.</param> 735 /// <param name="texture">Texture from which the descriptor should be used.</param> 736 public void CreateTextureIfInvalid(in TextureDesc desc, ref TextureHandle texture) 737 { 738 if (!texture.IsValid()) 739 texture = m_Resources.CreateTexture(desc); 740 } 741 742 /// <summary> 743 /// Gets the descriptor of the specified Texture resource. 744 /// </summary> 745 /// <param name="texture">Texture resource from which the descriptor is requested.</param> 746 /// <returns>The input texture descriptor.</returns> 747 public TextureDesc GetTextureDesc(TextureHandle texture) 748 { 749 return m_Resources.GetTextureResourceDesc(texture.handle); 750 } 751 752 /// <summary> 753 /// Gets the descriptor of the specified Texture resource. 754 /// </summary> 755 /// <param name="texture">Texture resource from which the descriptor is requested.</param> 756 /// <returns>The input texture descriptor.</returns> 757 public RenderTargetInfo GetRenderTargetInfo(TextureHandle texture) 758 { 759 RenderTargetInfo info; 760 m_Resources.GetRenderTargetInfo(texture.handle, out info); 761 return info; 762 } 763 764 765 /// <summary> 766 /// Creates a new Renderer List Render Graph resource. 767 /// </summary> 768 /// <param name="desc">Renderer List descriptor.</param> 769 /// <returns>A new RendererListHandle.</returns> 770 public RendererListHandle CreateRendererList(in CoreRendererListDesc desc) 771 { 772 return m_Resources.CreateRendererList(desc); 773 } 774 775 /// <summary> 776 /// Creates a new Renderer List Render Graph resource. 777 /// </summary> 778 /// <param name="desc">Renderer List descriptor.</param> 779 /// <returns>A new RendererListHandle.</returns> 780 public RendererListHandle CreateRendererList(in RendererListParams desc) 781 { 782 return m_Resources.CreateRendererList(desc); 783 } 784 785 /// <summary> 786 /// Creates a new Shadow Renderer List Render Graph resource. 787 /// </summary> 788 /// <param name="shadowDrawingSettings">DrawSettings that describe the shadow drawcall.</param> 789 /// <returns>A new RendererListHandle.</returns> 790 public RendererListHandle CreateShadowRendererList(ref ShadowDrawingSettings shadowDrawingSettings) 791 { 792 return m_Resources.CreateShadowRendererList(m_RenderGraphContext.renderContext, ref shadowDrawingSettings); 793 } 794 795 /// <summary> 796 /// Creates a new Gizmo Renderer List Render Graph resource. 797 /// </summary> 798 /// <param name="camera">The camera that is used for rendering the Gizmo.</param> 799 /// <param name="gizmoSubset">GizmoSubset that specifies whether gizmos render before or after postprocessing for a camera render. </param> 800 /// <returns>A new RendererListHandle.</returns> 801 public RendererListHandle CreateGizmoRendererList(in Camera camera, in GizmoSubset gizmoSubset) 802 { 803 return m_Resources.CreateGizmoRendererList(m_RenderGraphContext.renderContext, camera, gizmoSubset); 804 } 805 806 /// <summary> 807 /// Creates a new UIOverlay Renderer List Render Graph resource. 808 /// </summary> 809 /// <param name="camera">The camera that is used for rendering the full UIOverlay.</param> 810 /// <returns>A new RendererListHandle.</returns> 811 public RendererListHandle CreateUIOverlayRendererList(in Camera camera) 812 { 813 return m_Resources.CreateUIOverlayRendererList(m_RenderGraphContext.renderContext, camera, UISubset.All); 814 } 815 816 /// <summary> 817 /// Creates a new UIOverlay Renderer List Render Graph resource. 818 /// </summary> 819 /// <param name="camera">The camera that is used for rendering some subset of the UIOverlay.</param> 820 /// <param name="uiSubset">Enum flag that specifies which subset to render.</param> 821 /// <returns>A new RendererListHandle.</returns> 822 public RendererListHandle CreateUIOverlayRendererList(in Camera camera, in UISubset uiSubset) 823 { 824 return m_Resources.CreateUIOverlayRendererList(m_RenderGraphContext.renderContext, camera, uiSubset); 825 } 826 827 /// <summary> 828 /// Creates a new WireOverlay Renderer List Render Graph resource. 829 /// </summary> 830 /// <param name="camera">The camera that is used for rendering the WireOverlay.</param> 831 /// <returns>A new RendererListHandle.</returns> 832 public RendererListHandle CreateWireOverlayRendererList(in Camera camera) 833 { 834 return m_Resources.CreateWireOverlayRendererList(m_RenderGraphContext.renderContext, camera); 835 } 836 837 /// <summary> 838 /// Creates a new Skybox Renderer List Render Graph resource. 839 /// </summary> 840 /// <param name="camera">The camera that is used for rendering the Skybox.</param> 841 /// <returns>A new RendererListHandle.</returns> 842 public RendererListHandle CreateSkyboxRendererList(in Camera camera) 843 { 844 return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera); 845 } 846 847 /// <summary> 848 /// Creates a new Skybox Renderer List Render Graph resource. 849 /// </summary> 850 /// <param name="camera">The camera that is used for rendering the Skybox.</param> 851 /// <param name="projectionMatrix">The projection matrix used during XR rendering of the skybox.</param> 852 /// <param name="viewMatrix">The view matrix used during XR rendering of the skybox.</param> 853 /// <returns>A new RendererListHandle.</returns> 854 public RendererListHandle CreateSkyboxRendererList(in Camera camera, Matrix4x4 projectionMatrix, Matrix4x4 viewMatrix) 855 { 856 return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera, projectionMatrix, viewMatrix); 857 } 858 859 /// <summary> 860 /// Creates a new Skybox Renderer List Render Graph resource. 861 /// </summary> 862 /// <param name="camera">The camera that is used for rendering the Skybox.</param> 863 /// <param name="projectionMatrixL">The left eye projection matrix used during Legacy single pass XR rendering of the skybox.</param> 864 /// <param name="viewMatrixL">The left eye view matrix used during Legacy single pass XR rendering of the skybox.</param> 865 /// <param name="projectionMatrixR">The right eye projection matrix used during Legacy single pass XR rendering of the skybox.</param> 866 /// <param name="viewMatrixR">The right eye view matrix used during Legacy single pass XR rendering of the skybox.</param> 867 /// <returns>A new RendererListHandle.</returns> 868 public RendererListHandle CreateSkyboxRendererList(in Camera camera, Matrix4x4 projectionMatrixL, Matrix4x4 viewMatrixL, Matrix4x4 projectionMatrixR, Matrix4x4 viewMatrixR) 869 { 870 return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera, projectionMatrixL, viewMatrixL, projectionMatrixR, viewMatrixR); 871 } 872 873 /// <summary> 874 /// Import an external Graphics Buffer to the Render Graph. 875 /// Any pass writing to an imported graphics buffer will be considered having side effects and can't be automatically culled. 876 /// </summary> 877 /// <param name="graphicsBuffer">External Graphics Buffer that needs to be imported.</param> 878 /// <param name="forceRelease">The imported graphics buffer will be released after usage.</param> 879 /// <returns>A new GraphicsBufferHandle.</returns> 880 public BufferHandle ImportBuffer(GraphicsBuffer graphicsBuffer, bool forceRelease = false) 881 { 882 return m_Resources.ImportBuffer(graphicsBuffer, forceRelease); 883 } 884 885 /// <summary> 886 /// Create a new Render Graph Graphics Buffer resource. 887 /// </summary> 888 /// <param name="desc">Graphics Buffer descriptor.</param> 889 /// <returns>A new GraphicsBufferHandle.</returns> 890 public BufferHandle CreateBuffer(in BufferDesc desc) 891 { 892 return m_Resources.CreateBuffer(desc); 893 } 894 895 /// <summary> 896 /// Create a new Render Graph Graphics Buffer resource using the descriptor from another graphics buffer. 897 /// </summary> 898 /// <param name="graphicsBuffer">Graphics Buffer from which the descriptor should be used.</param> 899 /// <returns>A new GraphicsBufferHandle.</returns> 900 public BufferHandle CreateBuffer(in BufferHandle graphicsBuffer) 901 { 902 return m_Resources.CreateBuffer(m_Resources.GetBufferResourceDesc(graphicsBuffer.handle)); 903 } 904 905 /// <summary> 906 /// Gets the descriptor of the specified Graphics Buffer resource. 907 /// </summary> 908 /// <param name="graphicsBuffer">Graphics Buffer resource from which the descriptor is requested.</param> 909 /// <returns>The input graphics buffer descriptor.</returns> 910 public BufferDesc GetBufferDesc(in BufferHandle graphicsBuffer) 911 { 912 return m_Resources.GetBufferResourceDesc(graphicsBuffer.handle); 913 } 914 915 /// <summary> 916 /// Import an external RayTracingAccelerationStructure to the Render Graph. 917 /// Any pass writing to (building) an imported RayTracingAccelerationStructure will be considered having side effects and can't be automatically culled. 918 /// </summary> 919 /// <param name="accelStruct">External RayTracingAccelerationStructure that needs to be imported.</param> 920 /// <param name="name">Optional name for identifying the RayTracingAccelerationStructure in the Render Graph.</param> 921 /// <returns>A new RayTracingAccelerationStructureHandle.</returns> 922 public RayTracingAccelerationStructureHandle ImportRayTracingAccelerationStructure(in RayTracingAccelerationStructure accelStruct, string name = null) 923 { 924 return m_Resources.ImportRayTracingAccelerationStructure(accelStruct, name); 925 } 926 927 /// <summary> 928 /// Add a new Raster Render Pass to the Render Graph. Raster passes can execute rasterization workloads but cannot do other GPU work like copies or compute. 929 /// </summary> 930 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 931 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 932 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 933 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 934 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 935 /// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns> 936 public IRasterRenderGraphBuilder AddRasterRenderPass<PassData>(string passName, out PassData passData 937#if !CORE_PACKAGE_DOCTOOLS 938 , [CallerFilePath] string file = "", 939 [CallerLineNumber] int line = 0) where PassData : class, new() 940#endif 941 { 942 return AddRasterRenderPass(passName, out passData, GetDefaultProfilingSampler(passName), file, line); 943 } 944 945 /// <summary> 946 /// Add a new Raster Render Pass to the Render Graph. Raster passes can execute rasterization workloads but cannot do other GPU work like copies or compute. 947 /// </summary> 948 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 949 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 950 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 951 /// <param name="sampler">Profiling sampler used around the pass.</param> 952 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 953 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 954 /// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns> 955 public IRasterRenderGraphBuilder AddRasterRenderPass<PassData>(string passName, out PassData passData, ProfilingSampler sampler 956#if !CORE_PACKAGE_DOCTOOLS 957 ,[CallerFilePath] string file = "", 958 [CallerLineNumber] int line = 0) where PassData : class, new() 959#endif 960 { 961 AddPassDebugMetadata(passName, file, line); 962 963 var renderPass = m_RenderGraphPool.Get<RasterRenderGraphPass<PassData>>(); 964 renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Raster, sampler); 965 966 passData = renderPass.data; 967 968 m_RenderPasses.Add(renderPass); 969 970 m_builderInstance.Setup(renderPass, m_Resources, this); 971 return m_builderInstance; 972 } 973 974 /// <summary> 975 /// Add a new Compute Render Pass to the Render Graph. Raster passes can execute rasterization workloads but cannot do other GPU work like copies or compute. 976 /// </summary> 977 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 978 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 979 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 980 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 981 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 982 /// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns> 983 public IComputeRenderGraphBuilder AddComputePass<PassData>(string passName, out PassData passData 984#if !CORE_PACKAGE_DOCTOOLS 985 , [CallerFilePath] string file = "", 986 [CallerLineNumber] int line = 0) where PassData : class, new() 987#endif 988 { 989 return AddComputePass(passName, out passData, GetDefaultProfilingSampler(passName), file, line); 990 } 991 992 /// <summary> 993 /// Add a new Compute Render Pass to the Render Graph. Compute passes can execute compute workloads but cannot do rasterization. 994 /// </summary> 995 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 996 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 997 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 998 /// <param name="sampler">Profiling sampler used around the pass.</param> 999 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1000 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1001 /// <returns>A new instance of a IComputeRenderGraphBuilder used to setup the new Compute Render Pass.</returns> 1002 public IComputeRenderGraphBuilder AddComputePass<PassData>(string passName, out PassData passData, ProfilingSampler sampler 1003#if !CORE_PACKAGE_DOCTOOLS 1004 ,[CallerFilePath] string file = "", 1005 [CallerLineNumber] int line = 0) where PassData : class, new() 1006#endif 1007 { 1008 AddPassDebugMetadata(passName, file, line); 1009 1010 var renderPass = m_RenderGraphPool.Get<ComputeRenderGraphPass<PassData>>(); 1011 renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Compute, sampler); 1012 1013 passData = renderPass.data; 1014 1015 m_RenderPasses.Add(renderPass); 1016 1017 m_builderInstance.Setup(renderPass, m_Resources, this); 1018 return m_builderInstance; 1019 } 1020 1021 1022 /// <summary> 1023 /// Add a new Unsafe Render Pass to the Render Graph. Unsafe passes can do certain operations compute/raster render passes cannot do and have 1024 /// access to the full command buffer API. The unsafe API should be used sparingly as it has the following downsides: 1025 /// - Limited automatic validation of the commands and resource dependencies. The user is responsible to ensure that all dependencies are correctly declared. 1026 /// - All native render passes will be serialized out. 1027 /// - In the future the render graph compiler may generate a sub-optimal command stream for unsafe passes. 1028 /// When using a unsafe pass the graph will also not automatically set up graphics state like rendertargets. The pass should do this itself 1029 /// using cmd.SetRenderTarget and related commands. 1030 /// </summary> 1031 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 1032 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 1033 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 1034 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1035 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1036 /// <returns>A new instance of a IUnsafeRenderGraphBuilder used to setup the new Unsafe Render Pass.</returns> 1037 public IUnsafeRenderGraphBuilder AddUnsafePass<PassData>(string passName, out PassData passData 1038#if !CORE_PACKAGE_DOCTOOLS 1039 , [CallerFilePath] string file = "", 1040 [CallerLineNumber] int line = 0) where PassData : class, new() 1041#endif 1042 { 1043 return AddUnsafePass(passName, out passData, GetDefaultProfilingSampler(passName), file, line); 1044 } 1045 1046 1047 /// <summary> 1048 /// Add a new unsafe Render Pass to the Render Graph. Unsafe passes can do certain operations compute/raster render passes cannot do and have 1049 /// access to the full command buffer API. The unsafe API should be used sparingly as it has the following downsides: 1050 /// - Limited automatic validation of the commands and resource dependencies. The user is responsible to ensure that all dependencies are correctly declared. 1051 /// - All native render passes will be serialized out. 1052 /// - In the future the render graph compiler may generate a sub-optimal command stream for unsafe passes. 1053 /// When using an unsafe pass the graph will also not automatically set up graphics state like rendertargets. The pass should do this itself 1054 /// using cmd.SetRenderTarget and related commands. 1055 /// </summary> 1056 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 1057 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 1058 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 1059 /// <param name="sampler">Profiling sampler used around the pass.</param> 1060 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1061 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1062 /// <returns>A new instance of a IUnsafeRenderGraphBuilder used to setup the new unsafe Render Pass.</returns> 1063 public IUnsafeRenderGraphBuilder AddUnsafePass<PassData>(string passName, out PassData passData, ProfilingSampler sampler 1064#if !CORE_PACKAGE_DOCTOOLS 1065 , [CallerFilePath] string file = "", 1066 [CallerLineNumber] int line = 0) where PassData : class, new() 1067#endif 1068 { 1069 AddPassDebugMetadata(passName, file, line); 1070 1071 var renderPass = m_RenderGraphPool.Get<UnsafeRenderGraphPass<PassData>>(); 1072 renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Unsafe, sampler); 1073 renderPass.AllowGlobalState(true); 1074 1075 passData = renderPass.data; 1076 1077 m_RenderPasses.Add(renderPass); 1078 1079 m_builderInstance.Setup(renderPass, m_Resources, this); 1080 return m_builderInstance; 1081 } 1082 1083 /// <summary> 1084 /// Add a new Render Pass to the Render Graph. 1085 /// </summary> 1086 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 1087 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 1088 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 1089 /// <param name="sampler">Profiling sampler used around the pass.</param> 1090 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1091 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1092 /// <returns>A new instance of a RenderGraphBuilder used to setup the new Render Pass.</returns> 1093 public RenderGraphBuilder AddRenderPass<PassData>(string passName, out PassData passData, ProfilingSampler sampler 1094#if !CORE_PACKAGE_DOCTOOLS 1095 ,[CallerFilePath] string file = "", 1096 [CallerLineNumber] int line = 0) where PassData : class, new() 1097#endif 1098 { 1099 AddPassDebugMetadata(passName, file, line); 1100 1101 var renderPass = m_RenderGraphPool.Get<RenderGraphPass<PassData>>(); 1102 renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Legacy, sampler); 1103 renderPass.AllowGlobalState(true);// Old pass types allow global state by default as HDRP relies on it 1104 1105 passData = renderPass.data; 1106 1107 m_RenderPasses.Add(renderPass); 1108 1109 return new RenderGraphBuilder(renderPass, m_Resources, this); 1110 } 1111 1112 /// <summary> 1113 /// Add a new Render Pass to the Render Graph. 1114 /// </summary> 1115 /// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam> 1116 /// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param> 1117 /// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param> 1118 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1119 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1120 /// <returns>A new instance of a RenderGraphBuilder used to setup the new Render Pass.</returns> 1121 public RenderGraphBuilder AddRenderPass<PassData>(string passName, out PassData passData 1122#if !CORE_PACKAGE_DOCTOOLS 1123 ,[CallerFilePath] string file = "", 1124 [CallerLineNumber] int line = 0) where PassData : class, new() 1125#endif 1126 { 1127 return AddRenderPass(passName, out passData, GetDefaultProfilingSampler(passName), file, line); 1128 } 1129 1130 /// <summary> 1131 /// Starts the recording of the render graph. 1132 /// This must be called before adding any pass to the render graph. 1133 /// </summary> 1134 /// <param name="parameters">Parameters necessary for the render graph execution.</param> 1135 /// <example> 1136 /// <para>Begin recording the Render Graph.</para> 1137 /// <code> 1138 /// renderGraph.BeginRecording(parameters) 1139 /// // Add your render graph passes here. 1140 /// renderGraph.EndRecordingAndExecute() 1141 /// </code> 1142 /// </example> 1143 public void BeginRecording(in RenderGraphParameters parameters) 1144 { 1145 m_CurrentFrameIndex = parameters.currentFrameIndex; 1146 m_CurrentExecutionName = parameters.executionName != null ? parameters.executionName : "RenderGraphExecution"; 1147 m_HasRenderGraphBegun = true; 1148 // Cannot do renderer list culling with compilation caching because it happens after compilation is done so it can lead to discrepancies. 1149 m_RendererListCulling = parameters.rendererListCulling && !m_EnableCompilationCaching; 1150 1151 m_Resources.BeginRenderGraph(m_ExecutionCount++); 1152 1153 if (m_DebugParameters.enableLogging) 1154 { 1155 m_FrameInformationLogger.Initialize(m_CurrentExecutionName); 1156 } 1157 1158 m_DefaultResources.InitializeForRendering(this); 1159 1160 m_RenderGraphContext.cmd = parameters.commandBuffer; 1161 m_RenderGraphContext.renderContext = parameters.scriptableRenderContext; 1162 m_RenderGraphContext.contextlessTesting = parameters.invalidContextForTesting; 1163 m_RenderGraphContext.renderGraphPool = m_RenderGraphPool; 1164 m_RenderGraphContext.defaultResources = m_DefaultResources; 1165 1166 if (m_DebugParameters.immediateMode) 1167 { 1168 UpdateCurrentCompiledGraph(graphHash: -1, forceNoCaching: true); 1169 1170 LogFrameInformation(); 1171 1172 // Prepare the list of compiled pass info for immediate mode. 1173 // Conservative resize because we don't know how many passes there will be. 1174 // We might still need to grow the array later on anyway if it's not enough. 1175 m_CurrentCompiledGraph.compiledPassInfos.Resize(m_CurrentCompiledGraph.compiledPassInfos.capacity); 1176 m_CurrentImmediatePassIndex = 0; 1177 1178 for (int i = 0; i < (int)RenderGraphResourceType.Count; ++i) 1179 { 1180 if (m_ImmediateModeResourceList[i] == null) 1181 m_ImmediateModeResourceList[i] = new List<int>(); 1182 1183 m_ImmediateModeResourceList[i].Clear(); 1184 } 1185 1186 m_Resources.BeginExecute(m_CurrentFrameIndex); 1187 } 1188 } 1189 1190 /// <summary> 1191 /// Ends the recording and executes the render graph. 1192 /// This must be called once all passes have been added to the render graph. 1193 /// </summary> 1194 public void EndRecordingAndExecute() 1195 { 1196 Execute(); 1197 } 1198 1199 /// <summary> 1200 /// Execute the Render Graph in its current state. 1201 /// </summary> 1202 internal void Execute() 1203 { 1204 m_ExecutionExceptionWasRaised = false; 1205 1206 try 1207 { 1208#if DEVELOPMENT_BUILD || UNITY_EDITOR 1209 if (m_RenderGraphContext.cmd == null) 1210 throw new InvalidOperationException("RenderGraph.BeginRecording was not called before executing the render graph."); 1211#endif 1212 if (!m_DebugParameters.immediateMode) 1213 { 1214 LogFrameInformation(); 1215 1216 int graphHash = 0; 1217 if (m_EnableCompilationCaching) 1218 graphHash = ComputeGraphHash(); 1219 1220 if (nativeRenderPassesEnabled) 1221 CompileNativeRenderGraph(graphHash); 1222 else 1223 CompileRenderGraph(graphHash); 1224 1225 m_Resources.BeginExecute(m_CurrentFrameIndex); 1226 1227#if UNITY_EDITOR 1228 // Feeding Render Graph Viewer before resource deallocation at pass execution 1229 GenerateDebugData(); 1230#endif 1231 1232 if (nativeRenderPassesEnabled) 1233 ExecuteNativeRenderGraph(); 1234 else 1235 ExecuteRenderGraph(); 1236 1237 // Clear the shader bindings for all global textures to make sure bindings don't leak outside the graph 1238 ClearGlobalBindings(); 1239 } 1240 } 1241 catch (Exception e) 1242 { 1243 if (m_RenderGraphContext.contextlessTesting) 1244 { 1245 // Throw it for the tests to handle 1246 throw; 1247 } 1248 else 1249 { 1250 // If we're not testing log the exception and swallow it. 1251 // TODO: Do we really want to swallow exceptions here? Not a very c# thing to do. 1252 Debug.LogError("Render Graph Execution error"); 1253 if (!m_ExecutionExceptionWasRaised) // Already logged. TODO: There is probably a better way in C# to handle that. 1254 Debug.LogException(e); 1255 m_ExecutionExceptionWasRaised = true; 1256 } 1257 } 1258 finally 1259 { 1260 if (m_DebugParameters.immediateMode) 1261 ReleaseImmediateModeResources(); 1262 1263 ClearCompiledGraph(m_CurrentCompiledGraph, m_EnableCompilationCaching); 1264 1265 m_Resources.EndExecute(); 1266 1267 InvalidateContext(); 1268 1269 m_HasRenderGraphBegun = false; 1270 } 1271 } 1272 1273 class ProfilingScopePassData 1274 { 1275 public ProfilingSampler sampler; 1276 } 1277 1278 const string k_BeginProfilingSamplerPassName = "BeginProfile"; 1279 const string k_EndProfilingSamplerPassName = "EndProfile"; 1280 1281 /// <summary> 1282 /// Begin a profiling scope. 1283 /// </summary> 1284 /// <param name="sampler">Sampler used for profiling.</param> 1285 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1286 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1287 public void BeginProfilingSampler(ProfilingSampler sampler, 1288 [CallerFilePath] string file = "", 1289 [CallerLineNumber] int line = 0) 1290 { 1291 if (sampler == null) 1292 return; 1293 1294 using (var builder = AddRenderPass<ProfilingScopePassData>(k_BeginProfilingSamplerPassName, out var passData, (ProfilingSampler)null, file, line)) 1295 { 1296 passData.sampler = sampler; 1297 builder.AllowPassCulling(false); 1298 builder.GenerateDebugData(false); 1299 builder.SetRenderFunc((ProfilingScopePassData data, RenderGraphContext ctx) => 1300 { 1301 data.sampler.Begin(ctx.cmd); 1302 }); 1303 } 1304 } 1305 1306 /// <summary> 1307 /// End a profiling scope. 1308 /// </summary> 1309 /// <param name="sampler">Sampler used for profiling.</param> 1310 /// <param name="file">File name of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1311 /// <param name="line">File line of the source file this function is called from. Used for debugging. This parameter is automatically generated by the compiler. Users do not need to pass it.</param> 1312 public void EndProfilingSampler(ProfilingSampler sampler, 1313 [CallerFilePath] string file = "", 1314 [CallerLineNumber] int line = 0) 1315 { 1316 if (sampler == null) 1317 return; 1318 1319 using (var builder = AddRenderPass<ProfilingScopePassData>(k_EndProfilingSamplerPassName, out var passData, (ProfilingSampler)null, file, line)) 1320 { 1321 passData.sampler = sampler; 1322 builder.AllowPassCulling(false); 1323 builder.GenerateDebugData(false); 1324 builder.SetRenderFunc((ProfilingScopePassData data, RenderGraphContext ctx) => 1325 { 1326 data.sampler.End(ctx.cmd); 1327 }); 1328 } 1329 } 1330 1331 #endregion 1332 1333 #region Internal Interface 1334 // Internal for testing purpose only 1335 internal DynamicArray<CompiledPassInfo> GetCompiledPassInfos() { return m_CurrentCompiledGraph.compiledPassInfos; } 1336 1337 // Internal for testing purpose only 1338 internal void ClearCompiledGraph() 1339 { 1340 ClearCompiledGraph(m_CurrentCompiledGraph, false); 1341 } 1342 1343 void ClearCompiledGraph(CompiledGraph compiledGraph, bool useCompilationCaching) 1344 { 1345 ClearRenderPasses(); 1346 m_Resources.Clear(m_ExecutionExceptionWasRaised); 1347 m_RendererLists.Clear(); 1348 registeredGlobals.Clear(); 1349 1350 // When using compilation caching, we need to keep alive the result of the compiled graph. 1351 if (!useCompilationCaching) 1352 { 1353 if (!nativeRenderPassesEnabled) 1354 compiledGraph?.Clear(); 1355 } 1356 } 1357 1358 void InvalidateContext() 1359 { 1360 m_RenderGraphContext.cmd = null; 1361 m_RenderGraphContext.renderGraphPool = null; 1362 m_RenderGraphContext.defaultResources = null; 1363 } 1364 1365 internal void OnPassAdded(RenderGraphPass pass) 1366 { 1367 if (m_DebugParameters.immediateMode) 1368 { 1369 ExecutePassImmediately(pass); 1370 } 1371 } 1372 1373 internal delegate void OnGraphRegisteredDelegate(RenderGraph graph); 1374 internal static event OnGraphRegisteredDelegate onGraphRegistered; 1375 internal static event OnGraphRegisteredDelegate onGraphUnregistered; 1376 internal delegate void OnExecutionRegisteredDelegate(RenderGraph graph, string executionName); 1377 internal static event OnExecutionRegisteredDelegate onExecutionRegistered; 1378 internal static event OnExecutionRegisteredDelegate onExecutionUnregistered; 1379 internal static event Action onDebugDataCaptured; 1380 1381 #endregion 1382 1383 #region Private Interface 1384 1385 // Internal for testing purpose only. 1386 internal int ComputeGraphHash() 1387 { 1388 using (new ProfilingScope(ProfilingSampler.Get(RenderGraphProfileId.ComputeHashRenderGraph))) 1389 { 1390 var hash128 = HashFNV1A32.Create(); 1391 for (int i = 0; i < m_RenderPasses.Count; ++i) 1392 m_RenderPasses[i].ComputeHash(ref hash128, m_Resources); 1393 1394 return hash128.value; 1395 } 1396 } 1397 1398 void CountReferences() 1399 { 1400 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1401 var compiledResourceInfo = m_CurrentCompiledGraph.compiledResourcesInfos; 1402 1403 for (int passIndex = 0; passIndex < compiledPassInfo.size; ++passIndex) 1404 { 1405 RenderGraphPass pass = m_RenderPasses[passIndex]; 1406 ref CompiledPassInfo passInfo = ref compiledPassInfo[passIndex]; 1407 1408 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1409 { 1410 var resourceRead = pass.resourceReadLists[type]; 1411 foreach (var resource in resourceRead) 1412 { 1413 ref CompiledResourceInfo info = ref compiledResourceInfo[type][resource.index]; 1414 info.imported = m_Resources.IsRenderGraphResourceImported(resource); 1415 info.consumers.Add(passIndex); 1416 info.refCount++; 1417 1418#if DEVELOPMENT_BUILD || UNITY_EDITOR 1419 passInfo.debugResourceReads[type].Add(m_Resources.GetRenderGraphResourceName(resource)); 1420#endif 1421 } 1422 1423 var resourceWrite = pass.resourceWriteLists[type]; 1424 foreach (var resource in resourceWrite) 1425 { 1426 ref CompiledResourceInfo info = ref compiledResourceInfo[type][resource.index]; 1427 info.imported = m_Resources.IsRenderGraphResourceImported(resource); 1428 info.producers.Add(passIndex); 1429 1430 // Writing to an imported texture is considered as a side effect because we don't know what users will do with it outside of render graph. 1431 passInfo.hasSideEffect = info.imported; 1432 passInfo.refCount++; 1433 1434#if DEVELOPMENT_BUILD || UNITY_EDITOR 1435 passInfo.debugResourceWrites[type].Add(m_Resources.GetRenderGraphResourceName(resource)); 1436#endif 1437 } 1438 1439 foreach (var resource in pass.transientResourceList[type]) 1440 { 1441 ref CompiledResourceInfo info = ref compiledResourceInfo[type][resource.index]; 1442 info.refCount++; 1443 info.consumers.Add(passIndex); 1444 info.producers.Add(passIndex); 1445 } 1446 } 1447 } 1448 } 1449 1450 void CullUnusedPasses() 1451 { 1452 if (m_DebugParameters.disablePassCulling) 1453 { 1454 if (m_DebugParameters.enableLogging) 1455 { 1456 m_FrameInformationLogger.LogLine("- Pass Culling Disabled -\n"); 1457 } 1458 return; 1459 } 1460 1461 // This will cull all passes that produce resource that are never read. 1462 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1463 { 1464 DynamicArray<CompiledResourceInfo> resourceUsageList = m_CurrentCompiledGraph.compiledResourcesInfos[type]; 1465 1466 // Gather resources that are never read. 1467 m_CullingStack.Clear(); 1468 for (int i = 1; i < resourceUsageList.size; ++i) // 0 == null resource skip it 1469 { 1470 if (resourceUsageList[i].refCount == 0) 1471 { 1472 m_CullingStack.Push(i); 1473 } 1474 } 1475 1476 while (m_CullingStack.Count != 0) 1477 { 1478 var unusedResource = resourceUsageList[m_CullingStack.Pop()]; 1479 foreach (var producerIndex in unusedResource.producers) 1480 { 1481 ref var producerInfo = ref m_CurrentCompiledGraph.compiledPassInfos[producerIndex]; 1482 var producerPass = m_RenderPasses[producerIndex]; 1483 producerInfo.refCount--; 1484 if (producerInfo.refCount == 0 && !producerInfo.hasSideEffect && producerInfo.allowPassCulling) 1485 { 1486 // Producer is not necessary anymore as it produces zero resources 1487 // Cull it and decrement refCount of all the textures it reads. 1488 producerInfo.culled = true; 1489 1490 foreach (var resource in producerPass.resourceReadLists[type]) 1491 { 1492 ref CompiledResourceInfo resourceInfo = ref resourceUsageList[resource.index]; 1493 resourceInfo.refCount--; 1494 // If a resource is not used anymore, add it to the stack to be processed in subsequent iteration. 1495 if (resourceInfo.refCount == 0) 1496 m_CullingStack.Push(resource.index); 1497 } 1498 } 1499 } 1500 } 1501 } 1502 1503 LogCulledPasses(); 1504 } 1505 1506 void UpdatePassSynchronization(ref CompiledPassInfo currentPassInfo, ref CompiledPassInfo producerPassInfo, int currentPassIndex, int lastProducer, ref int intLastSyncIndex) 1507 { 1508 // Current pass needs to wait for pass index lastProducer 1509 currentPassInfo.syncToPassIndex = lastProducer; 1510 // Update latest pass waiting for the other pipe. 1511 intLastSyncIndex = lastProducer; 1512 1513 // Producer will need a graphics fence that this pass will wait on. 1514 producerPassInfo.needGraphicsFence = true; 1515 // We update the producer pass with the index of the smallest pass waiting for it. 1516 // This will be used to "lock" resource from being reused until the pipe has been synchronized. 1517 if (producerPassInfo.syncFromPassIndex == -1) 1518 producerPassInfo.syncFromPassIndex = currentPassIndex; 1519 } 1520 1521 void UpdateResourceSynchronization(ref int lastGraphicsPipeSync, ref int lastComputePipeSync, int currentPassIndex, in CompiledResourceInfo resource) 1522 { 1523 int lastProducer = GetLatestProducerIndex(currentPassIndex, resource); 1524 if (lastProducer != -1) 1525 { 1526 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1527 ref CompiledPassInfo currentPassInfo = ref compiledPassInfo[currentPassIndex]; 1528 1529 //If the passes are on different pipes, we need synchronization. 1530 if (m_CurrentCompiledGraph.compiledPassInfos[lastProducer].enableAsyncCompute != currentPassInfo.enableAsyncCompute) 1531 { 1532 // Pass is on compute pipe, need sync with graphics pipe. 1533 if (currentPassInfo.enableAsyncCompute) 1534 { 1535 if (lastProducer > lastGraphicsPipeSync) 1536 { 1537 UpdatePassSynchronization(ref currentPassInfo, ref compiledPassInfo[lastProducer], currentPassIndex, lastProducer, ref lastGraphicsPipeSync); 1538 } 1539 } 1540 else 1541 { 1542 if (lastProducer > lastComputePipeSync) 1543 { 1544 UpdatePassSynchronization(ref currentPassInfo, ref compiledPassInfo[lastProducer], currentPassIndex, lastProducer, ref lastComputePipeSync); 1545 } 1546 } 1547 } 1548 } 1549 } 1550 1551 int GetFirstValidConsumerIndex(int passIndex, in CompiledResourceInfo info) 1552 { 1553 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1554 // We want to know the lowest pass index after the current pass that reads from the resource. 1555 foreach (int consumer in info.consumers) 1556 { 1557 // consumers are by construction in increasing order. 1558 if (consumer > passIndex && !compiledPassInfo[consumer].culled) 1559 return consumer; 1560 } 1561 1562 return -1; 1563 } 1564 1565 int FindTextureProducer(int consumerPass, in CompiledResourceInfo info, out int index) 1566 { 1567 // We check all producers before the consumerPass. The first one not culled will be the one allocating the resource 1568 // If they are all culled, we need to get the one right before the consumer, it will allocate or reuse the resource 1569 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1570 int previousPass = 0; 1571 for (index = 0; index < info.producers.Count; index++) 1572 { 1573 int currentPass = info.producers[index]; 1574 // We found a valid producer - he will allocate the texture 1575 if (!compiledPassInfo[currentPass].culled) 1576 return currentPass; 1577 // We reached consumer pass, return last producer even if it's culled 1578 if (currentPass >= consumerPass) 1579 return previousPass; 1580 previousPass = currentPass; 1581 } 1582 1583 return previousPass; 1584 } 1585 1586 int GetLatestProducerIndex(int passIndex, in CompiledResourceInfo info) 1587 { 1588 // We want to know the highest pass index below the current pass that writes to the resource. 1589 int result = -1; 1590 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1591 foreach (var producer in info.producers) 1592 { 1593 var producerPassInfo = compiledPassInfo[producer]; 1594 // producers are by construction in increasing order. 1595 // We also need to make sure we don't return a pass that was culled (can happen at this point because of renderer list culling). 1596 if (producer < passIndex && !(producerPassInfo.culled || producerPassInfo.culledByRendererList)) 1597 result = producer; 1598 else 1599 return result; 1600 } 1601 1602 return result; 1603 } 1604 1605 int GetLatestValidReadIndex(in CompiledResourceInfo info) 1606 { 1607 if (info.consumers.Count == 0) 1608 return -1; 1609 1610 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1611 var consumers = info.consumers; 1612 for (int i = consumers.Count - 1; i >= 0; --i) 1613 { 1614 if (!compiledPassInfo[consumers[i]].culled) 1615 return consumers[i]; 1616 } 1617 1618 return -1; 1619 } 1620 1621 int GetFirstValidWriteIndex(in CompiledResourceInfo info) 1622 { 1623 if (info.producers.Count == 0) 1624 return -1; 1625 1626 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1627 var producers = info.producers; 1628 for (int i = 0; i < producers.Count; i++) 1629 { 1630 if (!compiledPassInfo[producers[i]].culled) 1631 return producers[i]; 1632 } 1633 1634 return -1; 1635 } 1636 1637 int GetLatestValidWriteIndex(in CompiledResourceInfo info) 1638 { 1639 if (info.producers.Count == 0) 1640 return -1; 1641 1642 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1643 var producers = info.producers; 1644 for (int i = producers.Count - 1; i >= 0; --i) 1645 { 1646 if (!compiledPassInfo[producers[i]].culled) 1647 return producers[i]; 1648 } 1649 1650 return -1; 1651 } 1652 1653 void CreateRendererLists() 1654 { 1655 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1656 for (int passIndex = 0; passIndex < compiledPassInfo.size; ++passIndex) 1657 { 1658 ref CompiledPassInfo passInfo = ref compiledPassInfo[passIndex]; 1659 1660 if (passInfo.culled) 1661 continue; 1662 1663 // Gather all renderer lists 1664 m_RendererLists.AddRange(m_RenderPasses[passInfo.index].usedRendererListList); 1665 } 1666 1667 // Anything related to renderer lists needs a real context to be able to use/test it 1668 Debug.Assert(m_RendererLists.Count == 0 || m_RenderGraphContext.contextlessTesting == false); 1669 1670 // Creates all renderer lists 1671 m_Resources.CreateRendererLists(m_RendererLists, m_RenderGraphContext.renderContext, m_RendererListCulling); 1672 } 1673 1674 internal bool GetImportedFallback(TextureDesc desc, out TextureHandle fallback) 1675 { 1676 fallback = TextureHandle.nullHandle; 1677 1678 // We don't have any fallback texture with MSAA 1679 if (!desc.bindTextureMS) 1680 { 1681 if (desc.depthBufferBits != DepthBits.None) 1682 { 1683 fallback = defaultResources.whiteTexture; 1684 } 1685 else if (desc.clearColor == Color.black || desc.clearColor == default) 1686 { 1687 if (desc.dimension == TextureXR.dimension) 1688 fallback = defaultResources.blackTextureXR; 1689 else if (desc.dimension == TextureDimension.Tex3D) 1690 fallback = defaultResources.blackTexture3DXR; 1691 else if (desc.dimension == TextureDimension.Tex2D) 1692 fallback = defaultResources.blackTexture; 1693 } 1694 else if (desc.clearColor == Color.white) 1695 { 1696 if (desc.dimension == TextureXR.dimension) 1697 fallback = defaultResources.whiteTextureXR; 1698 else if (desc.dimension == TextureDimension.Tex2D) 1699 fallback = defaultResources.whiteTexture; 1700 } 1701 } 1702 1703 return fallback.IsValid(); 1704 } 1705 1706 void AllocateCulledPassResources(ref CompiledPassInfo passInfo) 1707 { 1708 var passIndex = passInfo.index; 1709 var pass = m_RenderPasses[passIndex]; 1710 1711 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1712 { 1713 var resourcesInfo = m_CurrentCompiledGraph.compiledResourcesInfos[type]; 1714 foreach (var resourceHandle in pass.resourceWriteLists[type]) 1715 { 1716 ref var compiledResource = ref resourcesInfo[resourceHandle.index]; 1717 1718 // Check if there is a valid consumer and no other valid producer 1719 int consumerPass = GetFirstValidConsumerIndex(passIndex, compiledResource); 1720 int producerPass = FindTextureProducer(consumerPass, compiledResource, out int index); 1721 if (consumerPass != -1 && passIndex == producerPass) 1722 { 1723 if (type == (int)RenderGraphResourceType.Texture) 1724 { 1725 // Try to transform into an imported resource - for some textures, this will save an allocation 1726 // We have a way to disable the fallback, because we can't fallback to RenderTexture and sometimes it's necessary (eg. SampleCopyChannel_xyzw2x) 1727 var textureResource = m_Resources.GetTextureResource(resourceHandle); 1728 if (!textureResource.desc.disableFallBackToImportedTexture && GetImportedFallback(textureResource.desc, out var fallback)) 1729 { 1730 compiledResource.imported = true; 1731 textureResource.imported = true; 1732 textureResource.graphicsResource = m_Resources.GetTexture(fallback); 1733 continue; 1734 } 1735 1736 textureResource.desc.sizeMode = TextureSizeMode.Explicit; 1737 textureResource.desc.width = 1; 1738 textureResource.desc.height = 1; 1739 textureResource.desc.clearBuffer = true; 1740 } 1741 1742 // Delegate resource allocation to the consumer 1743 compiledResource.producers[index - 1] = consumerPass; 1744 } 1745 } 1746 } 1747 } 1748 1749 void UpdateResourceAllocationAndSynchronization() 1750 { 1751 int lastGraphicsPipeSync = -1; 1752 int lastComputePipeSync = -1; 1753 1754 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1755 var compiledResourceInfo = m_CurrentCompiledGraph.compiledResourcesInfos; 1756 1757 // First go through all passes. 1758 // - Update the last pass read index for each resource. 1759 // - Add texture to creation list for passes that first write to a texture. 1760 // - Update synchronization points for all resources between compute and graphics pipes. 1761 for (int passIndex = 0; passIndex < compiledPassInfo.size; ++passIndex) 1762 { 1763 ref CompiledPassInfo passInfo = ref compiledPassInfo[passIndex]; 1764 1765 // If this pass is culled, we need to make sure that any texture read by a later pass is still allocated 1766 // We also try to find an imported fallback to save an allocation 1767 if (passInfo.culledByRendererList) 1768 AllocateCulledPassResources(ref passInfo); 1769 1770 if (passInfo.culled) 1771 continue; 1772 1773 var pass = m_RenderPasses[passInfo.index]; 1774 1775 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1776 { 1777 var resourcesInfo = compiledResourceInfo[type]; 1778 foreach (var resource in pass.resourceReadLists[type]) 1779 { 1780 UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourcesInfo[resource.index]); 1781 } 1782 1783 foreach (var resource in pass.resourceWriteLists[type]) 1784 { 1785 UpdateResourceSynchronization(ref lastGraphicsPipeSync, ref lastComputePipeSync, passIndex, resourcesInfo[resource.index]); 1786 } 1787 } 1788 } 1789 1790 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1791 { 1792 var resourceInfos = compiledResourceInfo[type]; 1793 1794 // Now push resources to the release list of the pass that reads it last. 1795 for (int i = 1; i < resourceInfos.size; ++i) // 0 == null resource skip it 1796 { 1797 CompiledResourceInfo resourceInfo = resourceInfos[i]; 1798 1799 bool sharedResource = m_Resources.IsRenderGraphResourceShared((RenderGraphResourceType)type, i); 1800 bool forceRelease = m_Resources.IsRenderGraphResourceForceReleased((RenderGraphResourceType) type, i); 1801 1802 // Imported resource needs neither creation nor release. 1803 if (resourceInfo.imported && !sharedResource && !forceRelease) 1804 continue; 1805 1806 // Resource creation 1807 int firstWriteIndex = GetFirstValidWriteIndex(resourceInfo); 1808 // Index -1 can happen for imported resources (for example an imported dummy black texture will never be written to but does not need creation anyway) 1809 // Or when the only pass that was writting to this resource was culled dynamically by renderer lists 1810 if (firstWriteIndex != -1) 1811 compiledPassInfo[firstWriteIndex].resourceCreateList[type].Add(i); 1812 1813 var latestValidReadIndex = GetLatestValidReadIndex(resourceInfo); 1814 var latestValidWriteIndex = GetLatestValidWriteIndex(resourceInfo); 1815 1816 // Sometimes, a texture can be written by a pass after the last pass that reads it. 1817 // In this case, we need to extend its lifetime to this pass otherwise the pass would get an invalid texture. 1818 // This is exhibited in cases where a pass might produce more than one output and one of them isn't used. 1819 // Ex: Transparent pass in HDRP that writes to the color buffer and motion vectors. 1820 // If TAA/MotionBlur aren't used, the movecs are never read after the transparent pass and it would raise this error. 1821 // Because of that, it's hard to make this an actual error. 1822 // Commented out code to check such cases if needed. 1823 //if (latestValidReadIndex != -1 && (latestValidWriteIndex > latestValidReadIndex)) 1824 //{ 1825 // var name = m_Resources.GetRenderGraphResourceName((RenderGraphResourceType)type, i); 1826 // var lastPassReadName = m_CompiledPassInfos[latestValidReadIndex].pass.name; 1827 // var lastPassWriteName = m_CompiledPassInfos[latestValidWriteIndex].pass.name; 1828 // Debug.LogError($"Resource {name} is written again after the last pass that reads it.\nLast pass read: {lastPassReadName}\nLast pass write: {lastPassWriteName}"); 1829 //} 1830 1831 // For not imported resources, make sure we don't try to release them if they were never created (due to culling). 1832 bool shouldRelease = !(firstWriteIndex == -1 && !resourceInfo.imported); 1833 int lastReadPassIndex = shouldRelease ? Math.Max(latestValidWriteIndex, latestValidReadIndex) : -1; 1834 1835 // Texture release 1836 if (lastReadPassIndex != -1) 1837 { 1838 // In case of async passes, we need to extend lifetime of resource to the first pass on the graphics pipeline that wait for async passes to be over. 1839 // Otherwise, if we freed the resource right away during an async pass, another non async pass could reuse the resource even though the async pipe is not done. 1840 if (compiledPassInfo[lastReadPassIndex].enableAsyncCompute) 1841 { 1842 int currentPassIndex = lastReadPassIndex; 1843 int firstWaitingPassIndex = compiledPassInfo[currentPassIndex].syncFromPassIndex; 1844 // Find the first async pass that is synchronized by the graphics pipeline (ie: passInfo.syncFromPassIndex != -1) 1845 while (firstWaitingPassIndex == -1 && currentPassIndex++ < compiledPassInfo.size - 1) 1846 { 1847 if (compiledPassInfo[currentPassIndex].enableAsyncCompute) 1848 firstWaitingPassIndex = compiledPassInfo[currentPassIndex].syncFromPassIndex; 1849 } 1850 1851 // Fail safe in case render graph is badly formed. 1852 if (currentPassIndex == compiledPassInfo.size) 1853 { 1854 // This is not true with passes with side effect as they are writing to a resource that may not be read by the render graph this frame and to no other resource. 1855 // In this case we extend the lifetime of resources to the end of the frame. It's not idea but it should not be the majority of cases. 1856 if (compiledPassInfo[lastReadPassIndex].hasSideEffect) 1857 { 1858 firstWaitingPassIndex = currentPassIndex; 1859 } 1860 else 1861 { 1862 RenderGraphPass invalidPass = m_RenderPasses[lastReadPassIndex]; 1863 1864 var resName = "<unknown>"; 1865#if DEVELOPMENT_BUILD || UNITY_EDITOR 1866 resName = m_Resources.GetRenderGraphResourceName((RenderGraphResourceType)type, i); 1867#endif 1868 var msg = $"{(RenderGraphResourceType)type} resource '{resName}' in asynchronous pass '{invalidPass.name}' is missing synchronization on the graphics pipeline."; 1869 throw new InvalidOperationException(msg); 1870 } 1871 } 1872 1873 // Finally add the release command to the pass before the first pass that waits for the compute pipe. 1874 var releasePassIndex = Math.Max(0, firstWaitingPassIndex - 1); 1875 1876 // Check to ensure that we do not release resources on a culled pass (causes a leak otherwise). 1877 while (compiledPassInfo[releasePassIndex].culled) 1878 releasePassIndex = Math.Max(0, releasePassIndex - 1); 1879 1880 ref CompiledPassInfo passInfo = ref compiledPassInfo[releasePassIndex]; 1881 passInfo.resourceReleaseList[type].Add(i); 1882 } 1883 else 1884 { 1885 ref CompiledPassInfo passInfo = ref compiledPassInfo[lastReadPassIndex]; 1886 passInfo.resourceReleaseList[type].Add(i); 1887 } 1888 } 1889 1890 if (sharedResource && (firstWriteIndex != -1 || lastReadPassIndex != -1)) // A shared resource is considered used if it's either read or written at any pass. 1891 { 1892 m_Resources.UpdateSharedResourceLastFrameIndex(type, i); 1893 } 1894 } 1895 } 1896 } 1897 1898 void UpdateAllSharedResourceLastFrameIndex() 1899 { 1900 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 1901 { 1902 var resourceInfos = m_CurrentCompiledGraph.compiledResourcesInfos[type]; 1903 var sharedResourceCount = m_Resources.GetSharedResourceCount((RenderGraphResourceType)type); 1904 1905 for (int i = 1; i <= sharedResourceCount; ++i) // 0 == null resource skip it 1906 { 1907 CompiledResourceInfo resourceInfo = resourceInfos[i]; 1908 var latestValidReadIndex = GetLatestValidReadIndex(resourceInfo); 1909 int firstWriteIndex = GetFirstValidWriteIndex(resourceInfo); 1910 1911 if ((firstWriteIndex != -1 || latestValidReadIndex != -1)) // A shared resource is considered used if it's either read or written at any pass. 1912 { 1913 m_Resources.UpdateSharedResourceLastFrameIndex(type, i); 1914 } 1915 } 1916 } 1917 } 1918 1919 bool AreRendererListsEmpty(List<RendererListHandle> rendererLists) 1920 { 1921 // Anything related to renderer lists needs a real context to be able to use/test it 1922 Debug.Assert(m_RenderGraphContext.contextlessTesting == false); 1923 1924 foreach (RendererListHandle handle in rendererLists) 1925 { 1926 var rendererList = m_Resources.GetRendererList(handle); 1927 if (m_RenderGraphContext.renderContext.QueryRendererListStatus(rendererList) == RendererListStatus.kRendererListPopulated) 1928 { 1929 return false; 1930 } 1931 } 1932 1933 // If the list of RendererLists is empty, then the default behavior is to not cull, so return false. 1934 return rendererLists.Count > 0 ? true : false; 1935 } 1936 1937 void TryCullPassAtIndex(int passIndex) 1938 { 1939 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1940 ref var compiledPass = ref compiledPassInfo[passIndex]; 1941 var pass = m_RenderPasses[passIndex]; 1942 if (!compiledPass.culled && 1943 pass.allowPassCulling && 1944 pass.allowRendererListCulling && 1945 !compiledPass.hasSideEffect) 1946 { 1947 if (AreRendererListsEmpty(pass.usedRendererListList)) 1948 { 1949 //Debug.Log($"Culling pass <color=red> {pass.name} </color>"); 1950 compiledPass.culled = compiledPass.culledByRendererList = true; 1951 } 1952 } 1953 } 1954 1955 void CullRendererLists() 1956 { 1957 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 1958 for (int passIndex = 0; passIndex < compiledPassInfo.size; ++passIndex) 1959 { 1960 var compiledPass = compiledPassInfo[passIndex]; 1961 1962 if (!compiledPass.culled && !compiledPass.hasSideEffect) 1963 { 1964 var pass = m_RenderPasses[passIndex]; 1965 if (pass.usedRendererListList.Count > 0) 1966 { 1967 TryCullPassAtIndex(passIndex); 1968 } 1969 } 1970 } 1971 } 1972 1973 bool UpdateCurrentCompiledGraph(int graphHash, bool forceNoCaching = false) 1974 { 1975 bool cached = false; 1976 if (m_EnableCompilationCaching && !forceNoCaching) 1977 cached = m_CompilationCache.GetCompilationCache(graphHash, m_ExecutionCount, out m_CurrentCompiledGraph); 1978 else 1979 m_CurrentCompiledGraph = m_DefaultCompiledGraph; 1980 1981 return cached; 1982 } 1983 1984 // Internal visibility for testing purpose only 1985 // Traverse the render graph: 1986 // - Determines when resources are created/released 1987 // - Determines async compute pass synchronization 1988 // - Cull unused render passes. 1989 internal void CompileRenderGraph(int graphHash) 1990 { 1991 using (new ProfilingScope(m_RenderGraphContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.CompileRenderGraph))) 1992 { 1993 bool compilationIsCached = UpdateCurrentCompiledGraph(graphHash); 1994 1995 if (!compilationIsCached) 1996 { 1997 m_CurrentCompiledGraph.Clear(); 1998 m_CurrentCompiledGraph.InitializeCompilationData(m_RenderPasses, m_Resources); 1999 CountReferences(); 2000 2001 // First cull all passes that produce unused output 2002 CullUnusedPasses(); 2003 } 2004 2005 // Create the renderer lists of the remaining passes 2006 CreateRendererLists(); 2007 2008 if (!compilationIsCached) 2009 { 2010 // Cull dynamically the graph passes based on the renderer list visibility 2011 if (m_RendererListCulling) 2012 CullRendererLists(); 2013 2014 // After all culling passes, allocate the resources for this frame 2015 UpdateResourceAllocationAndSynchronization(); 2016 } 2017 else 2018 { 2019 // We need to update all shared resource frame index usage otherwise they might not be in a valid state. 2020 // Otherwise it's done in UpdateResourceAllocationAndSynchronization(). 2021 UpdateAllSharedResourceLastFrameIndex(); 2022 } 2023 2024 LogRendererListsCreation(); 2025 } 2026 } 2027 2028 ref CompiledPassInfo CompilePassImmediatly(RenderGraphPass pass) 2029 { 2030 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 2031 // If we don't have enough pre allocated elements we double the size. 2032 // It's pretty aggressive but the immediate mode is only for debug purpose so it should be fine. 2033 if (m_CurrentImmediatePassIndex >= compiledPassInfo.size) 2034 compiledPassInfo.Resize(compiledPassInfo.size * 2); 2035 2036 ref CompiledPassInfo passInfo = ref compiledPassInfo[m_CurrentImmediatePassIndex++]; 2037 passInfo.Reset(pass, m_CurrentImmediatePassIndex - 1); 2038 // In immediate mode we don't have proper information to generate synchronization so we disable async compute. 2039 passInfo.enableAsyncCompute = false; 2040 2041 // In immediate mode, we don't have any resource usage information so we'll just create resources whenever they are written to if not already alive. 2042 // We will release all resources at the end of the render graph execution. 2043 for (int iType = 0; iType < (int)RenderGraphResourceType.Count; ++iType) 2044 { 2045 foreach (var res in pass.transientResourceList[iType]) 2046 { 2047 passInfo.resourceCreateList[iType].Add(res.index); 2048 passInfo.resourceReleaseList[iType].Add(res.index); 2049 2050#if DEVELOPMENT_BUILD || UNITY_EDITOR 2051 passInfo.debugResourceWrites[iType].Add(m_Resources.GetRenderGraphResourceName(res)); 2052 passInfo.debugResourceReads[iType].Add(m_Resources.GetRenderGraphResourceName(res)); 2053#endif 2054 } 2055 2056 foreach (var res in pass.resourceWriteLists[iType]) 2057 { 2058 if (pass.transientResourceList[iType].Contains(res)) 2059 continue; // Prevent registering writes to transient texture twice 2060 2061 if (!m_Resources.IsGraphicsResourceCreated(res)) 2062 { 2063 passInfo.resourceCreateList[iType].Add(res.index); 2064 m_ImmediateModeResourceList[iType].Add(res.index); 2065 } 2066 2067#if DEVELOPMENT_BUILD || UNITY_EDITOR 2068 passInfo.debugResourceWrites[iType].Add(m_Resources.GetRenderGraphResourceName(res)); 2069#endif 2070 } 2071 2072 foreach (var res in pass.resourceReadLists[iType]) 2073 { 2074#if DEVELOPMENT_BUILD || UNITY_EDITOR 2075 passInfo.debugResourceReads[iType].Add(m_Resources.GetRenderGraphResourceName(res)); 2076#endif 2077 } 2078 } 2079 2080 // Create the necessary renderer lists 2081 foreach (var rl in pass.usedRendererListList) 2082 { 2083 if (!m_Resources.IsRendererListCreated(rl)) 2084 m_RendererLists.Add(rl); 2085 } 2086 // Anything related to renderer lists needs a real context to be able to use/test it 2087 Debug.Assert(m_RenderGraphContext.contextlessTesting == false); 2088 m_Resources.CreateRendererLists(m_RendererLists, m_RenderGraphContext.renderContext); 2089 m_RendererLists.Clear(); 2090 2091 return ref passInfo; 2092 } 2093 2094 void ExecutePassImmediately(RenderGraphPass pass) 2095 { 2096 ExecuteCompiledPass(ref CompilePassImmediatly(pass)); 2097 } 2098 2099 void ExecuteCompiledPass(ref CompiledPassInfo passInfo) 2100 { 2101 if (passInfo.culled) 2102 return; 2103 2104 var pass = m_RenderPasses[passInfo.index]; 2105 2106 if (!pass.HasRenderFunc()) 2107 throw new InvalidOperationException($"RenderPass {pass.name} was not provided with an execute function."); 2108 2109 try 2110 { 2111 using (new ProfilingScope(m_RenderGraphContext.cmd, pass.customSampler)) 2112 { 2113 LogRenderPassBegin(passInfo); 2114 using (new RenderGraphLogIndent(m_FrameInformationLogger)) 2115 { 2116 m_RenderGraphContext.executingPass = pass; 2117 PreRenderPassExecute(passInfo, pass, m_RenderGraphContext); 2118 pass.Execute(m_RenderGraphContext); 2119 PostRenderPassExecute(ref passInfo, pass, m_RenderGraphContext); 2120 } 2121 } 2122 } 2123 catch (Exception e) 2124 { 2125 // Dont log errors during tests 2126 if (m_RenderGraphContext.contextlessTesting == false) 2127 { 2128 // Log exception from the pass that raised it to have improved error logging quality for users 2129 m_ExecutionExceptionWasRaised = true; 2130 Debug.LogError($"Render Graph execution error at pass '{pass.name}' ({passInfo.index})"); 2131 Debug.LogException(e); 2132 } 2133 throw; 2134 } 2135 } 2136 2137 // Execute the compiled render graph 2138 void ExecuteRenderGraph() 2139 { 2140 using (new ProfilingScope(m_RenderGraphContext.cmd, ProfilingSampler.Get(RenderGraphProfileId.ExecuteRenderGraph))) 2141 { 2142 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 2143 for (int passIndex = 0; passIndex < compiledPassInfo.size; ++passIndex) 2144 { 2145 ExecuteCompiledPass(ref compiledPassInfo[passIndex]); 2146 } 2147 } 2148 } 2149 2150 void PreRenderPassSetRenderTargets(in CompiledPassInfo passInfo, RenderGraphPass pass, InternalRenderGraphContext rgContext) 2151 { 2152 var depthBufferIsValid = pass.depthAccess.textureHandle.IsValid(); 2153 if (depthBufferIsValid || pass.colorBufferMaxIndex != -1) 2154 { 2155 var colorBufferAccess = pass.colorBufferAccess; 2156 if (pass.colorBufferMaxIndex > 0) 2157 { 2158 var mrtArray = m_TempMRTArrays[pass.colorBufferMaxIndex]; 2159 2160 for (int i = 0; i <= pass.colorBufferMaxIndex; ++i) 2161 { 2162#if DEVELOPMENT_BUILD || UNITY_EDITOR 2163 if (!colorBufferAccess[i].textureHandle.IsValid()) 2164 throw new InvalidOperationException("MRT setup is invalid. Some indices are not used."); 2165#endif 2166 mrtArray[i] = m_Resources.GetTexture(colorBufferAccess[i].textureHandle); 2167 } 2168 2169 if (depthBufferIsValid) 2170 { 2171 CoreUtils.SetRenderTarget(rgContext.cmd, mrtArray, m_Resources.GetTexture(pass.depthAccess.textureHandle)); 2172 } 2173 else 2174 { 2175 throw new InvalidOperationException("Setting MRTs without a depth buffer is not supported."); 2176 } 2177 } 2178 else 2179 { 2180 if (depthBufferIsValid) 2181 { 2182 if (pass.colorBufferMaxIndex > -1) 2183 { 2184 CoreUtils.SetRenderTarget(rgContext.cmd, m_Resources.GetTexture(pass.colorBufferAccess[0].textureHandle), 2185 m_Resources.GetTexture(pass.depthAccess.textureHandle)); 2186 } 2187 else 2188 { 2189 CoreUtils.SetRenderTarget(rgContext.cmd, m_Resources.GetTexture(pass.depthAccess.textureHandle)); 2190 } 2191 } 2192 else 2193 { 2194 if (pass.colorBufferAccess[0].textureHandle.IsValid()) 2195 { 2196 CoreUtils.SetRenderTarget(rgContext.cmd, m_Resources.GetTexture(pass.colorBufferAccess[0].textureHandle)); 2197 } 2198 else 2199 throw new InvalidOperationException("Neither Depth nor color render targets are correctly setup at pass " + pass.name + "."); 2200 } 2201 } 2202 } 2203 } 2204 2205 void PreRenderPassExecute(in CompiledPassInfo passInfo, RenderGraphPass pass, InternalRenderGraphContext rgContext) 2206 { 2207 // Need to save the command buffer to restore it later as the one in the context can changed if running a pass async. 2208 m_PreviousCommandBuffer = rgContext.cmd; 2209 2210 bool executedWorkDuringResourceCreation = false; 2211 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 2212 { 2213 foreach (int resource in passInfo.resourceCreateList[type]) 2214 { 2215 executedWorkDuringResourceCreation |= m_Resources.CreatePooledResource(rgContext, type, resource); 2216 } 2217 } 2218 2219 if (passInfo.enableFoveatedRasterization) 2220 rgContext.cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled); 2221 2222 PreRenderPassSetRenderTargets(passInfo, pass, rgContext); 2223 2224 if (passInfo.enableAsyncCompute) 2225 { 2226 GraphicsFence previousFence = new GraphicsFence(); 2227 if (executedWorkDuringResourceCreation) 2228 { 2229 previousFence = rgContext.cmd.CreateGraphicsFence(GraphicsFenceType.AsyncQueueSynchronisation, SynchronisationStageFlags.AllGPUOperations); 2230 } 2231 2232 // Flush current command buffer on the render context before enqueuing async commands. 2233 if (rgContext.contextlessTesting == false) 2234 rgContext.renderContext.ExecuteCommandBuffer(rgContext.cmd); 2235 rgContext.cmd.Clear(); 2236 2237 CommandBuffer asyncCmd = CommandBufferPool.Get(pass.name); 2238 asyncCmd.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute); 2239 rgContext.cmd = asyncCmd; 2240 2241 if (executedWorkDuringResourceCreation) 2242 { 2243 rgContext.cmd.WaitOnAsyncGraphicsFence(previousFence); 2244 } 2245 } 2246 2247 // Synchronize with graphics or compute pipe if needed. 2248 if (passInfo.syncToPassIndex != -1) 2249 { 2250 rgContext.cmd.WaitOnAsyncGraphicsFence(m_CurrentCompiledGraph.compiledPassInfos[passInfo.syncToPassIndex].fence); 2251 } 2252 } 2253 2254 void PostRenderPassExecute(ref CompiledPassInfo passInfo, RenderGraphPass pass, InternalRenderGraphContext rgContext) 2255 { 2256 foreach (var tex in pass.setGlobalsList) 2257 { 2258 rgContext.cmd.SetGlobalTexture(tex.Item2, tex.Item1); 2259 } 2260 2261 if (passInfo.needGraphicsFence) 2262 passInfo.fence = rgContext.cmd.CreateAsyncGraphicsFence(); 2263 2264 if (passInfo.enableAsyncCompute) 2265 { 2266 // Anything related to async command buffer execution needs a real context to be able to use/test it 2267 // As the async will likely be waited for but never finish if there is no real context 2268 Debug.Assert(m_RenderGraphContext.contextlessTesting == false); 2269 2270 // The command buffer has been filled. We can kick the async task. 2271 rgContext.renderContext.ExecuteCommandBufferAsync(rgContext.cmd, ComputeQueueType.Background); 2272 CommandBufferPool.Release(rgContext.cmd); 2273 rgContext.cmd = m_PreviousCommandBuffer; // Restore the main command buffer. 2274 } 2275 2276 if (passInfo.enableFoveatedRasterization) 2277 rgContext.cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled); 2278 2279 m_RenderGraphPool.ReleaseAllTempAlloc(); 2280 2281 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 2282 { 2283 foreach (var resource in passInfo.resourceReleaseList[type]) 2284 { 2285 m_Resources.ReleasePooledResource(rgContext, type, resource); 2286 } 2287 } 2288 } 2289 2290 void ClearRenderPasses() 2291 { 2292 foreach (var pass in m_RenderPasses) 2293 pass.Release(m_RenderGraphPool); 2294 m_RenderPasses.Clear(); 2295 } 2296 2297 void ReleaseImmediateModeResources() 2298 { 2299 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 2300 { 2301 foreach (var resource in m_ImmediateModeResourceList[type]) 2302 { 2303 m_Resources.ReleasePooledResource(m_RenderGraphContext, type, resource); 2304 } 2305 } 2306 } 2307 2308 void LogFrameInformation() 2309 { 2310 if (m_DebugParameters.enableLogging) 2311 { 2312 m_FrameInformationLogger.LogLine($"==== Staring render graph frame for: {m_CurrentExecutionName} ===="); 2313 2314 if (!m_DebugParameters.immediateMode) 2315 m_FrameInformationLogger.LogLine("Number of passes declared: {0}\n", m_RenderPasses.Count); 2316 } 2317 } 2318 2319 void LogRendererListsCreation() 2320 { 2321 if (m_DebugParameters.enableLogging) 2322 { 2323 m_FrameInformationLogger.LogLine("Number of renderer lists created: {0}\n", m_RendererLists.Count); 2324 } 2325 } 2326 2327 void LogRenderPassBegin(in CompiledPassInfo passInfo) 2328 { 2329 if (m_DebugParameters.enableLogging) 2330 { 2331 RenderGraphPass pass = m_RenderPasses[passInfo.index]; 2332 2333 m_FrameInformationLogger.LogLine("[{0}][{1}] \"{2}\"", pass.index, pass.enableAsyncCompute ? "Compute" : "Graphics", pass.name); 2334 using (new RenderGraphLogIndent(m_FrameInformationLogger)) 2335 { 2336 if (passInfo.syncToPassIndex != -1) 2337 m_FrameInformationLogger.LogLine("Synchronize with [{0}]", passInfo.syncToPassIndex); 2338 } 2339 } 2340 } 2341 2342 void LogCulledPasses() 2343 { 2344 if (m_DebugParameters.enableLogging) 2345 { 2346 m_FrameInformationLogger.LogLine("Pass Culling Report:"); 2347 using (new RenderGraphLogIndent(m_FrameInformationLogger)) 2348 { 2349 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 2350 for (int i = 0; i < compiledPassInfo.size; ++i) 2351 { 2352 if (compiledPassInfo[i].culled) 2353 { 2354 var pass = m_RenderPasses[i]; 2355 m_FrameInformationLogger.LogLine("[{0}] {1}", pass.index, pass.name); 2356 } 2357 } 2358 m_FrameInformationLogger.LogLine("\n"); 2359 } 2360 } 2361 } 2362 2363 ProfilingSampler GetDefaultProfilingSampler(string name) 2364 { 2365 // In non-dev builds, ProfilingSampler.Get returns null, so we'd always end up executing this. 2366 // To avoid that we also ifdef the code out here. 2367#if DEVELOPMENT_BUILD || UNITY_EDITOR 2368 int hash = name.GetHashCode(); 2369 if (!m_DefaultProfilingSamplers.TryGetValue(hash, out var sampler)) 2370 { 2371 sampler = new ProfilingSampler(name); 2372 m_DefaultProfilingSamplers.Add(hash, sampler); 2373 } 2374 2375 return sampler; 2376#else 2377 return null; 2378#endif 2379 } 2380 2381 void UpdateImportedResourceLifeTime(ref DebugData.ResourceData data, List<int> passList) 2382 { 2383 foreach (var pass in passList) 2384 { 2385 if (data.creationPassIndex == -1) 2386 data.creationPassIndex = pass; 2387 else 2388 data.creationPassIndex = Math.Min(data.creationPassIndex, pass); 2389 2390 if (data.releasePassIndex == -1) 2391 data.releasePassIndex = pass; 2392 else 2393 data.releasePassIndex = Math.Max(data.releasePassIndex, pass); 2394 } 2395 } 2396 2397 void GenerateDebugData() 2398 { 2399 if (m_ExecutionExceptionWasRaised) 2400 return; 2401 2402 if (!isRenderGraphViewerActive) 2403 { 2404 CleanupDebugData(); 2405 return; 2406 } 2407 2408 if (!m_DebugData.TryGetValue(m_CurrentExecutionName, out var debugData)) 2409 { 2410 onExecutionRegistered?.Invoke(this, m_CurrentExecutionName); 2411 debugData = new DebugData(); 2412 m_DebugData.Add(m_CurrentExecutionName, debugData); 2413 return; // Generate the debug data on the next frame, because script metadata is collected during recording step 2414 } 2415 2416 // Only generate debug data on request 2417 if (m_CaptureDebugDataForExecution == null || !m_CaptureDebugDataForExecution.Equals(m_CurrentExecutionName)) 2418 return; 2419 2420 debugData.Clear(); 2421 2422 if (nativeRenderPassesEnabled) 2423 nativeCompiler.GenerateNativeCompilerDebugData(ref debugData); 2424 else 2425 GenerateCompilerDebugData(ref debugData); 2426 2427 onDebugDataCaptured?.Invoke(); 2428 2429 m_CaptureDebugDataForExecution = null; 2430 2431 ClearPassDebugMetadata(); 2432 } 2433 2434 void GenerateCompilerDebugData(ref DebugData debugData) 2435 { 2436 var compiledPassInfo = m_CurrentCompiledGraph.compiledPassInfos; 2437 var compiledResourceInfo = m_CurrentCompiledGraph.compiledResourcesInfos; 2438 2439 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 2440 { 2441 for (int i = 0; i < compiledResourceInfo[type].size; ++i) 2442 { 2443 ref var resourceInfo = ref compiledResourceInfo[type][i]; 2444 DebugData.ResourceData newResource = new DebugData.ResourceData(); 2445 if (i != 0) 2446 { 2447 var resName = m_Resources.GetRenderGraphResourceName((RenderGraphResourceType)type, i); 2448 newResource.name = !string.IsNullOrEmpty(resName) ? resName : "(unnamed)"; 2449 newResource.imported = m_Resources.IsRenderGraphResourceImported((RenderGraphResourceType)type, i); 2450 } 2451 else 2452 { 2453 // The above functions will throw exceptions when used with the null argument so just use a dummy instead 2454 newResource.name = "<null>"; 2455 newResource.imported = true; 2456 } 2457 2458 newResource.creationPassIndex = -1; 2459 newResource.releasePassIndex = -1; 2460 2461 RenderGraphResourceType resourceType = (RenderGraphResourceType) type; 2462 var handle = new ResourceHandle(i, resourceType, false); 2463 if (i != 0 && handle.IsValid()) 2464 { 2465 if (resourceType == RenderGraphResourceType.Texture) 2466 { 2467 m_Resources.GetRenderTargetInfo(handle, out var renderTargetInfo); 2468 2469 var textureData = new DebugData.TextureResourceData(); 2470 textureData.width = renderTargetInfo.width; 2471 textureData.height = renderTargetInfo.height; 2472 textureData.depth = renderTargetInfo.volumeDepth; 2473 textureData.samples = renderTargetInfo.msaaSamples; 2474 textureData.format = renderTargetInfo.format; 2475 2476 newResource.textureData = textureData; 2477 } 2478 else if (resourceType == RenderGraphResourceType.Buffer) 2479 { 2480 var bufferDesc = m_Resources.GetBufferResourceDesc(handle, true); 2481 2482 var bufferData = new DebugData.BufferResourceData(); 2483 bufferData.count = bufferDesc.count; 2484 bufferData.stride = bufferDesc.stride; 2485 bufferData.target = bufferDesc.target; 2486 bufferData.usage = bufferDesc.usageFlags; 2487 2488 newResource.bufferData = bufferData; 2489 } 2490 } 2491 2492 newResource.consumerList = new List<int>(resourceInfo.consumers); 2493 newResource.producerList = new List<int>(resourceInfo.producers); 2494 2495 if (newResource.imported) 2496 { 2497 UpdateImportedResourceLifeTime(ref newResource, newResource.consumerList); 2498 UpdateImportedResourceLifeTime(ref newResource, newResource.producerList); 2499 } 2500 2501 debugData.resourceLists[type].Add(newResource); 2502 } 2503 } 2504 2505 for (int i = 0; i < compiledPassInfo.size; ++i) 2506 { 2507 ref CompiledPassInfo passInfo = ref compiledPassInfo[i]; 2508 RenderGraphPass pass = m_RenderPasses[passInfo.index]; 2509 2510 DebugData.PassData newPass = new DebugData.PassData(); 2511 newPass.name = pass.name; 2512 newPass.type = pass.type; 2513 newPass.culled = passInfo.culled; 2514 newPass.async = passInfo.enableAsyncCompute; 2515 newPass.generateDebugData = pass.generateDebugData; 2516 newPass.resourceReadLists = new List<int>[(int)RenderGraphResourceType.Count]; 2517 newPass.resourceWriteLists = new List<int>[(int)RenderGraphResourceType.Count]; 2518 newPass.syncFromPassIndex = passInfo.syncFromPassIndex; 2519 newPass.syncToPassIndex = passInfo.syncToPassIndex; 2520 2521 DebugData.s_PassScriptMetadata.TryGetValue(pass.name, out newPass.scriptInfo); 2522 2523 for (int type = 0; type < (int)RenderGraphResourceType.Count; ++type) 2524 { 2525 newPass.resourceReadLists[type] = new List<int>(); 2526 newPass.resourceWriteLists[type] = new List<int>(); 2527 2528 foreach (var resourceRead in pass.resourceReadLists[type]) 2529 newPass.resourceReadLists[type].Add(resourceRead.index); 2530 foreach (var resourceWrite in pass.resourceWriteLists[type]) 2531 newPass.resourceWriteLists[type].Add(resourceWrite.index); 2532 2533 foreach (var resourceCreate in passInfo.resourceCreateList[type]) 2534 { 2535 var res = debugData.resourceLists[type][resourceCreate]; 2536 if (res.imported) 2537 continue; 2538 res.creationPassIndex = i; 2539 debugData.resourceLists[type][resourceCreate] = res; 2540 } 2541 2542 foreach (var resourceRelease in passInfo.resourceReleaseList[type]) 2543 { 2544 var res = debugData.resourceLists[type][resourceRelease]; 2545 if (res.imported) 2546 continue; 2547 res.releasePassIndex = i; 2548 debugData.resourceLists[type][resourceRelease] = res; 2549 } 2550 } 2551 2552 debugData.passList.Add(newPass); 2553 } 2554 } 2555 2556 void CleanupDebugData() 2557 { 2558 foreach (var kvp in m_DebugData) 2559 { 2560 onExecutionUnregistered?.Invoke(this, kvp.Key); 2561 } 2562 2563 m_DebugData.Clear(); 2564 } 2565 2566 #endregion 2567 2568 2569 Dictionary<int, TextureHandle> registeredGlobals = new Dictionary<int, TextureHandle>(); 2570 2571 internal void SetGlobal(TextureHandle h, int globalPropertyId) 2572 { 2573 if (!h.IsValid()) 2574 throw new ArgumentException("Attempting to register an invalid texture handle as a global"); 2575 2576 registeredGlobals[globalPropertyId] = h; 2577 } 2578 2579 internal bool IsGlobal(int globalPropertyId) 2580 { 2581 return registeredGlobals.ContainsKey(globalPropertyId); 2582 } 2583 2584 internal Dictionary<int, TextureHandle>.ValueCollection AllGlobals() 2585 { 2586 return registeredGlobals.Values; 2587 } 2588 2589 internal TextureHandle GetGlobal(int globalPropertyId) 2590 { 2591 TextureHandle h; 2592 registeredGlobals.TryGetValue(globalPropertyId, out h); 2593 return h; 2594 } 2595 2596 /// <summary> 2597 /// Clears the shader bindings associated with the registered globals in the graph 2598 /// 2599 /// This prevents later rendering logic from accidentally relying on stale shader bindings that were set 2600 /// earlier during graph execution. 2601 /// </summary> 2602 internal void ClearGlobalBindings() 2603 { 2604 // Set all the global texture shader bindings to the default black texture. 2605 // This doesn't technically "clear" the shader bindings, but it's the closest we can do. 2606 foreach (var globalTex in registeredGlobals) 2607 { 2608 m_RenderGraphContext.cmd.SetGlobalTexture(globalTex.Key, defaultResources.blackTexture); 2609 } 2610 } 2611 } 2612 2613 /// <summary> 2614 /// Render Graph Scoped Profiling markers 2615 /// </summary> 2616 [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] 2617 public struct RenderGraphProfilingScope : IDisposable 2618 { 2619#if DEVELOPMENT_BUILD || UNITY_EDITOR 2620 ProfilingSampler m_Sampler; 2621 RenderGraph m_RenderGraph; 2622 bool m_Disposed; 2623#endif 2624 2625 /// <summary> 2626 /// Profiling Scope constructor 2627 /// </summary> 2628 /// <param name="renderGraph">Render Graph used for this scope.</param> 2629 /// <param name="sampler">Profiling Sampler to be used for this scope.</param> 2630 public RenderGraphProfilingScope(RenderGraph renderGraph, ProfilingSampler sampler) 2631 { 2632#if DEVELOPMENT_BUILD || UNITY_EDITOR 2633 m_RenderGraph = renderGraph; 2634 m_Sampler = sampler; 2635 m_Disposed = false; 2636 renderGraph.BeginProfilingSampler(sampler); 2637#endif 2638 } 2639 2640 /// <summary> 2641 /// Dispose pattern implementation 2642 /// </summary> 2643 public void Dispose() 2644 { 2645 Dispose(true); 2646 } 2647 2648 // Protected implementation of Dispose pattern. 2649 void Dispose(bool disposing) 2650 { 2651#if DEVELOPMENT_BUILD || UNITY_EDITOR 2652 if (m_Disposed) 2653 return; 2654 2655 // As this is a struct, it could have been initialized using an empty constructor so we 2656 // need to make sure `cmd` isn't null to avoid a crash. Switching to a class would fix 2657 // this but will generate garbage on every frame (and this struct is used quite a lot). 2658 if (disposing) 2659 { 2660 m_RenderGraph.EndProfilingSampler(m_Sampler); 2661 } 2662 2663 m_Disposed = true; 2664#endif 2665 } 2666 } 2667}