Files
GhostEngine/Ghost.RenderGraph.Concept/Unity/RenderGraph.cs
Misaki 1fc9df1812 GhostEngine Render Graph: major refactor & Unity RG ref
- Major architectural refactor for performance, extensibility, and feature completeness: resource pooling, pass culling, aliasing, and compilation caching.
- Introduces type-safe builder and context APIs, blackboard pattern, and unified resource management.
- Adds detailed documentation and cleans up obsolete files and APIs.
- Includes (commented) Unity Render Graph source for reference; not compiled, for parity and future extension.
2026-01-11 23:43:17 +09:00

1692 lines
82 KiB
C#

#if false
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Scripting.APIUpdating;
// Typedef for the in-engine RendererList API (to avoid conflicts with the experimental version)
using CoreRendererListDesc = UnityEngine.Rendering.RendererUtils.RendererListDesc;
namespace UnityEngine.Rendering.RenderGraphModule
{
/// <summary>
/// Sets the read and write access for the depth buffer.
/// </summary>
[Flags][MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public enum DepthAccess
{
///<summary>Read Access.</summary>
Read = 1 << 0,
///<summary>Write Access.</summary>
Write = 1 << 1,
///<summary>Read and Write Access.</summary>
ReadWrite = Read | Write,
}
/// <summary>
/// Express the operations the rendergraph pass will do on a resource.
/// </summary>
[Flags][MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public enum AccessFlags
{
///<summary>The pass does not access the resource at all. Calling Use* functions with none has no effect.</summary>
None = 0,
///<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>
Read = 1 << 0,
///<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>
Write = 1 << 1,
///<summary>Previous data in the resource is not preserved. The resource will contain undefined data at the beginning of the pass.</summary>
Discard = 1 << 2,
///<summary>All data in the resource will be written by this pass. Data in the resource should never be read.</summary>
WriteAll = Write | Discard,
///<summary> Shortcut for Read | Write</summary>
ReadWrite = Read | Write
}
/// <summary>
/// Expresses additional pass properties that can be used to perform optimizations on some platforms.
/// </summary>
[Flags]
public enum ExtendedFeatureFlags
{
///<summary>Default state with no extended features enabled.</summary>
None = 0,
///<summary>On Meta XR, this flag can be set for the pass that performs the most 3D rendering to achieve better performance.</summary>
TileProperties = 1 << 0,
///<summary>On XR, this flag can be set for passes that are compatible with Multiview Render Regions</summary>
MultiviewRenderRegionsCompatible = 1 << 1,
///<summary>On Meta XR, this flag can be set to use MSAA shader resolve in the last subpass of a render pass. </summary>
MultisampledShaderResolve = 1 << 2,
}
[Flags]
internal enum RenderGraphState
{
/// <summary>
/// Render Graph is not doing anything.
/// </summary>
Idle = 0,
/// <summary>
/// Render Graph is recording the graph.
/// </summary>
RecordingGraph = 1 << 0,
/// <summary>
/// Render Graph is recording a low level pass.
/// </summary>
RecordingPass = 1 << 1,
/// <summary>
/// Render Graph is executing the graph.
/// </summary>
Executing = 1 << 2,
/// <summary>
/// Utility flag to check if the graph is active.
/// </summary>
Active = RecordingGraph | RecordingPass | Executing
}
/// <summary>
/// The strategy that the render pipeline should use to determine the UV origin of RenderTextures who have an Unknown TextureUVOrigin when rendering.
/// </summary>
public enum RenderTextureUVOriginStrategy
{
/// <summary>RenderTextures are always treated as bottom left orientation.</summary>
BottomLeft,
/// <summary>RenderTextures may inherit the backbuffer attachment orientation if they are only used via attachment reads.</summary>
PropagateAttachmentOrientation
}
/// <summary>
/// An object representing the internal context of a rendergraph pass execution.
/// This object is public for technical reasons only and should not be used.
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public class InternalRenderGraphContext
{
internal ScriptableRenderContext renderContext;
internal CommandBuffer cmd;
internal RenderGraphObjectPool renderGraphPool;
internal RenderGraphDefaultResources defaultResources;
internal RenderGraphPass executingPass;
internal NativeRenderPassCompiler.CompilerContextData compilerContext;
internal bool contextlessTesting;
internal bool forceResourceCreation;
}
// InternalRenderGraphContext is public (but all members are internal)
// only because the C# standard says that all interface member function implementations must be public.
// So below in for example the RasterGraphContext we can't implement the (internal) interface as
// internal void FromInternalContext(InternalRenderGraphContext context) { ... }
// So we have to make FromInternalContext public so InternalRenderGraphContext also becomes public.
// This seems an oversight in c# where Interfaces used as Generic constraints could very well be useful
// with internal only functions.
/// <summary>
/// Interface implemented by the different render graph contexts provided at execution timeline (via SetRenderFunc())
/// </summary>
internal interface IDerivedRendergraphContext
{
/// <summary>
/// This function is only public for techical resons of the c# language and should not be called outside the package.
/// </summary>
/// <param name="context">The context to convert</param>
public void FromInternalContext(InternalRenderGraphContext context);
/// <summary>
/// Retrieves the TextureUVOrigin of the Render Graph texture from its handle.
/// </summary>
/// <remarks>
/// This function can only be called when using the Native Render Pass Compiler (enabled by default).
/// </remarks>
/// <param name="textureHandle">The texture handle to query.</param>
/// <returns>The TextureUVOrigin of the texture.</returns>
public TextureUVOrigin GetTextureUVOrigin(in TextureHandle textureHandle);
}
/// <summary>
/// This class declares the context object passed to the execute function of a raster render pass.
/// <see cref="RenderGraph.AddRasterRenderPass"/>
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public struct RasterGraphContext : IDerivedRendergraphContext
{
private InternalRenderGraphContext wrappedContext;
///<summary>Command Buffer used for rendering.</summary>
public RasterCommandBuffer cmd;
///<summary>Render Graph default resources.</summary>
public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; }
///<summary>Render Graph pool used for temporary data.</summary>
public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; }
static internal RasterCommandBuffer rastercmd = new RasterCommandBuffer(null, null, false);
/// <inheritdoc />
public void FromInternalContext(InternalRenderGraphContext context)
{
wrappedContext = context;
rastercmd.m_WrappedCommandBuffer = wrappedContext.cmd;
rastercmd.m_ExecutingPass = context.executingPass;
cmd = rastercmd;
}
/// <inheritdoc />
public readonly TextureUVOrigin GetTextureUVOrigin(in TextureHandle textureHandle)
{
if (!SystemInfo.graphicsUVStartsAtTop)
return TextureUVOrigin.BottomLeft;
if (wrappedContext.compilerContext != null)
{
return wrappedContext.compilerContext.GetTextureUVOrigin(textureHandle);
}
return TextureUVOrigin.BottomLeft;
}
}
/// <summary>
/// This class declares the context object passed to the execute function of a compute render pass.
/// <see cref="RenderGraph.AddComputePass"/>
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public class ComputeGraphContext : IDerivedRendergraphContext
{
private InternalRenderGraphContext wrappedContext;
///<summary>Command Buffer used for rendering.</summary>
public ComputeCommandBuffer cmd;
///<summary>Render Graph default resources.</summary>
public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; }
///<summary>Render Graph pool used for temporary data.</summary>
public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; }
static internal ComputeCommandBuffer computecmd = new ComputeCommandBuffer(null, null, false);
/// <inheritdoc />
public void FromInternalContext(InternalRenderGraphContext context)
{
wrappedContext = context;
computecmd.m_WrappedCommandBuffer = wrappedContext.cmd;
computecmd.m_ExecutingPass = context.executingPass;
cmd = computecmd;
}
/// <inheritdoc />
public TextureUVOrigin GetTextureUVOrigin(in TextureHandle textureHandle)
{
if (!SystemInfo.graphicsUVStartsAtTop)
return TextureUVOrigin.BottomLeft;
if (wrappedContext.compilerContext != null)
{
return wrappedContext.compilerContext.GetTextureUVOrigin(textureHandle);
}
return TextureUVOrigin.BottomLeft;
}
}
/// <summary>
/// This class declares the context object passed to the execute function of an unsafe render pass.
/// <see cref="RenderGraph.AddUnsafePass"/>
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public class UnsafeGraphContext : IDerivedRendergraphContext
{
private InternalRenderGraphContext wrappedContext;
///<summary>Unsafe Command Buffer used for rendering.</summary>
public UnsafeCommandBuffer cmd;
///<summary>Render Graph default resources.</summary>
public RenderGraphDefaultResources defaultResources { get => wrappedContext.defaultResources; }
///<summary>Render Graph pool used for temporary data.</summary>
public RenderGraphObjectPool renderGraphPool { get => wrappedContext.renderGraphPool; }
internal static UnsafeCommandBuffer unsCmd = new UnsafeCommandBuffer(null, null, false);
/// <inheritdoc />
public void FromInternalContext(InternalRenderGraphContext context)
{
wrappedContext = context;
unsCmd.m_WrappedCommandBuffer = wrappedContext.cmd;
unsCmd.m_ExecutingPass = context.executingPass;
cmd = unsCmd;
}
/// <inheritdoc />
public TextureUVOrigin GetTextureUVOrigin(in TextureHandle textureHandle)
{
if (!SystemInfo.graphicsUVStartsAtTop)
return TextureUVOrigin.BottomLeft;
if (wrappedContext.compilerContext != null)
{
return wrappedContext.compilerContext.GetTextureUVOrigin(textureHandle);
}
return TextureUVOrigin.BottomLeft;
}
}
/// <summary>
/// This struct contains properties which control the execution of the Render Graph.
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public struct RenderGraphParameters
{
///<summary>Identifier for this render graph execution.</summary>
[Obsolete("Not used anymore. The debugging tools use the name of the object identified by executionId. #from(6000.3)")]
public string executionName;
///<summary>Identifier for this render graph execution (i.e. EntityId of the Camera rendering). Used for debugging tools.</summary>
public EntityId executionId;
///<summary>Whether the execution should generate debug data and be visible in Render Graph Viewer.</summary>
public bool generateDebugData;
///<summary>Index of the current frame being rendered.</summary>
public int currentFrameIndex;
///<summary> Controls whether to enable Renderer List culling or not.</summary>
[Obsolete("Not supported anymore. Syncing with culling system brings performance regressions in most cases. #from(6000.5)")]
public bool rendererListCulling;
///<summary>Scriptable Render Context used by the render pipeline.</summary>
public ScriptableRenderContext scriptableRenderContext;
///<summary>Command Buffer used to execute graphic commands.</summary>
public CommandBuffer commandBuffer;
///<summary>When running tests indicate the context is intentionally invalid and all calls on it should just do nothing.
///This allows you to run tests that rely on code execution the way to the pass render functions
///This also changes some behaviours with exception handling and error logging so the test framework can act on exceptions to validate behaviour better.</summary>
internal bool invalidContextForTesting;
///<summary>The strategy that the rendergraph should use to determine the texture uv origin if Unknown of RenderTextures when rendering.</summary>
public RenderTextureUVOriginStrategy renderTextureUVOriginStrategy;
}
/// <summary>
/// The Render Pass rendering delegate to use with typed contexts.
/// </summary>
/// <typeparam name="PassData">The type of the class used to provide data to the Render Pass.</typeparam>
/// <typeparam name="ContextType">The type of the context that will be passed to the render function.</typeparam>
/// <param name="data">Render Pass specific data.</param>
/// <param name="renderGraphContext">Global Render Graph context.</param>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public delegate void BaseRenderFunc<PassData, ContextType>(PassData data, ContextType renderGraphContext) where PassData : class, new();
/// <summary>
/// This class is the main entry point of the Render Graph system.
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public partial class RenderGraph
{
///<summary>Maximum number of MRTs supported by Render Graph.</summary>
public static readonly int kMaxMRTCount = 8;
/// <summary>
/// Enable the use of the render pass API instead of the traditional SetRenderTarget workflow for AddRasterRenderPass() API. Enabled by default since 6000.3. No alternative since 6000.5.
/// </summary>
/// <remarks>
/// When enabled, the render graph try to use render passes and supasses instead of relying on SetRendertarget. It
/// will try to aggressively optimize the number of BeginRenderPass+EndRenderPass calls as well as calls to NextSubPass.
/// This with the aim to maximize the time spent "on chip" on tile based renderers.
///
/// The Graph will automatically determine when to break render passes as well as the load and store actions to apply to these render passes.
/// 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
/// 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
/// a later pass this render target will be stored (and possibly resolved) and the render pass will be broken up.
///
/// 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
/// native render pases. In particular:
/// - The ImportBackbuffer overload without a RenderTargetInfo argument.
/// - Any AddRenderPass overloads. The more specific AddRasterRenderPass/AddComputePass/AddUnsafePass functions should be used to register passes.
///
/// 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
/// to new errors when using existing render graph code with nativeRenderPassesEnabled.
///
/// Note: that CommandBuffer.BeginRenderPass/EndRenderPass calls are different by design from SetRenderTarget so this could also have
/// 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).
/// </remarks>
[Obsolete("RenderGraph always enables native render pass support. #from(6000.5)")]
public bool nativeRenderPassesEnabled { get; set; } = true;
internal/*for tests*/ RenderGraphResourceRegistry m_Resources;
internal/*for tests*/ RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool();
RenderGraphBuilders m_builderInstance = new RenderGraphBuilders();
internal/*for tests*/ List<RenderGraphPass> m_RenderPasses = new List<RenderGraphPass>(64);
List<RendererListHandle> m_RendererLists = new List<RendererListHandle>(32);
RenderGraphDebugParams m_DebugParameters = new RenderGraphDebugParams();
RenderGraphDefaultResources m_DefaultResources = new RenderGraphDefaultResources();
Dictionary<int, ProfilingSampler> m_DefaultProfilingSamplers = new Dictionary<int, ProfilingSampler>();
InternalRenderGraphContext m_RenderGraphContext = new InternalRenderGraphContext();
CommandBuffer m_PreviousCommandBuffer;
RenderGraphCompilationCache m_CompilationCache;
EntityId m_CurrentExecutionId;
bool m_CurrentExecutionCanGenerateDebugData;
int m_ExecutionCount;
int m_CurrentFrameIndex;
bool m_ExecutionExceptionWasRaised;
bool m_EnableCompilationCaching;
internal/*for tests*/ static bool? s_EnableCompilationCachingForTests;
RenderGraphState m_RenderGraphState;
RenderTextureUVOriginStrategy m_renderTextureUVOriginStrategy;
// Global container of registered render graphs, associated with the list of executions that have been registered for them.
// When a RenderGraph is created, an entry is added to this dictionary. When that RenderGraph renders something,
// and a debug session is active, an entry is added to the list of executions for that RenderGraph using the executionId
// as the key. So when you render multiple times with the same executionId, only one DebugExecutionItem is created.
static Dictionary<RenderGraph, List<DebugExecutionItem>> s_RegisteredExecutions = new ();
#region Public Interface
/// <summary>Name of the Render Graph.</summary>
public string name { get; private set; } = "RenderGraph";
internal RenderGraphState RenderGraphState
{
get { return m_RenderGraphState; }
set { m_RenderGraphState = value; }
}
/// <summary>The strategy the Render Graph will take for the uv origin of RenderTextures in the graph.</summary>
public RenderTextureUVOriginStrategy renderTextureUVOriginStrategy
{
get { return m_renderTextureUVOriginStrategy; }
internal set { m_renderTextureUVOriginStrategy = value; }
}
/// <summary>If true, the Render Graph Viewer is active.</summary>
public static bool isRenderGraphViewerActive => RenderGraphDebugSession.hasActiveDebugSession;
/// <summary>If true, the Render Graph will run its various validity checks while processing (not considered in release mode).</summary>
internal static bool enableValidityChecks { get; private set; }
/// <summary>
/// Set of default resources usable in a pass rendering code.
/// </summary>
public RenderGraphDefaultResources defaultResources
{
get
{
return m_DefaultResources;
}
}
/// <summary>
/// Render Graph constructor.
/// </summary>
/// <param name="name">Optional name used to identify the render graph instance.</param>
public RenderGraph(string name = "RenderGraph")
{
this.name = name;
if (GraphicsSettings.TryGetRenderPipelineSettings<RenderGraphGlobalSettings>(out var renderGraphGlobalSettings))
{
m_EnableCompilationCaching = renderGraphGlobalSettings.enableCompilationCaching;
enableValidityChecks = renderGraphGlobalSettings.enableValidityChecks;
}
else // No SRP pipeline is present/active, it can happen with unit tests
{
enableValidityChecks = true;
}
if (s_EnableCompilationCachingForTests.HasValue)
m_EnableCompilationCaching = s_EnableCompilationCachingForTests.Value;
if (m_EnableCompilationCaching)
m_CompilationCache = new RenderGraphCompilationCache();
m_Resources = new RenderGraphResourceRegistry(m_DebugParameters);
RegisterGraph();
m_RenderGraphState = RenderGraphState.Idle;
RenderGraph.RenderGraphExceptionMessages.enableCaller = true;
#if !UNITY_EDITOR && DEVELOPMENT_BUILD
if (RenderGraphDebugSession.currentDebugSession == null)
RenderGraphDebugSession.Create<RenderGraphPlayerRemoteDebugSession>();
#endif
}
// Internal, only for testing
// Useful when we need to clean when calling
// internal functions in tests even if Render Graph is active
// This API shouldn't be called when the render graph is active!
internal void CleanupResourcesAndGraph()
{
// Usually done at the end of Execute step
// Also doing it here in case RG stopped before it
ClearCurrentCompiledGraph();
m_Resources.Cleanup();
m_DefaultResources.Cleanup();
m_RenderGraphPool.Cleanup();
nativeCompiler?.Cleanup();
m_CompilationCache?.Clear();
DelegateHashCodeUtils.ClearCache();
}
/// <summary>
/// Free up all resources used internally by the Render Graph instance, and unregister it so it won't be visible in the Render Graph Viewer.
/// </summary>
public void Cleanup()
{
CheckNotUsedWhenActive();
// Dispose of the compiled graphs left over in the cache
m_CompilationCache?.Cleanup();
CleanupResourcesAndGraph();
UnregisterGraph();
}
internal RenderGraphDebugParams debugParams => m_DebugParameters;
internal List<DebugUI.Widget> GetWidgetList()
{
return m_DebugParameters.GetWidgetList(name);
}
internal bool areAnySettingsActive => m_DebugParameters.AreAnySettingsActive;
/// <summary>
/// Register the render graph to the debug window.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph is active, please call it outside of RecordRenderGraph().
/// </remarks>
/// <param name="panel">Optional debug panel to which the render graph debug parameters will be registered.</param>
public void RegisterDebug(DebugUI.Panel panel = null)
{
CheckNotUsedWhenActive();
m_DebugParameters.RegisterDebug(name, panel);
}
/// <summary>
/// Unregister render graph from the debug window.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph is active, please call it outside of RecordRenderGraph().
/// </remarks>
public void UnRegisterDebug()
{
CheckNotUsedWhenActive();
m_DebugParameters.UnRegisterDebug(this.name);
}
/// <summary>
/// Get the list of all registered render graphs.
/// </summary>
/// <returns>The list of all registered render graphs.</returns>
public static List<RenderGraph> GetRegisteredRenderGraphs()
{
return new List<RenderGraph>(s_RegisteredExecutions.Keys);
}
internal static Dictionary<RenderGraph, List<DebugExecutionItem>> GetRegisteredExecutions() => s_RegisteredExecutions;
/// <summary>
/// End frame processing. Purge resources that have been used since last frame and resets internal states.
/// This need to be called once per frame.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph is active, please call it outside of RecordRenderGraph().
/// </remarks>
public void EndFrame()
{
CheckNotUsedWhenActive();
m_Resources.PurgeUnusedGraphicsResources();
}
/// <summary>
/// Import an external texture to the Render Graph.
/// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="rt">External RTHandle that needs to be imported.</param>
/// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns>
public TextureHandle ImportTexture(RTHandle rt)
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportTexture(rt);
}
/// <summary>
/// Import an external Variable Rate Shading (VRS) textures to the RenderGraph.
/// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled.
/// </summary>
/// <param name="rt">External shading rate image RTHandle that needs to be imported.</param>
/// <returns>New TextureHandle that represents the imported shading rate images in the context of this rendergraph.</returns>
public TextureHandle ImportShadingRateImageTexture(RTHandle rt)
{
if (ShadingRateInfo.supportsPerImageTile)
return m_Resources.ImportTexture(rt);
return TextureHandle.nullHandle;
}
/// <summary>
/// Import an external texture to the Render Graph.
/// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled.
///
/// Note: RTHandles that wrap RenderTargetIdentifier will fail to import using this overload as render graph can't derive the render texture's properties.
/// In that case the overload taking a RenderTargetInfo argument should be used instead.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="rt">External RTHandle that needs to be imported.</param>
/// <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>
/// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns>
public TextureHandle ImportTexture(RTHandle rt, ImportResourceParams importParams)
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportTexture(rt, importParams);
}
/// <summary>
/// Import an external texture to the Render Graph. This overload should be used for RTHandles wrapping a RenderTargetIdentifier.
/// If the RTHandle is wrapping a RenderTargetIdentifer, Rendergraph can't derive the render texture's properties so the user has to provide this info to the graph through RenderTargetInfo.
///
/// Any pass writing to an imported texture will be considered having side effects and can't be automatically culled.
///
/// 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.
/// If this is not the case, the overload of ImportTexture without a RenderTargetInfo argument should be used instead.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="rt">External RTHandle that needs to be imported.</param>
/// <param name="info">The properties of the passed in RTHandle.</param>
/// <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>
/// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns>
public TextureHandle ImportTexture(RTHandle rt, RenderTargetInfo info, ImportResourceParams importParams = new ImportResourceParams())
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportTexture(rt, info, importParams);
}
/// <summary>
/// Import an external texture to the Render Graph and set the handle as builtin handle. This can only happen from within the graph module
/// so it is internal.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="rt">External RTHandle that needs to be imported.</param>
/// <param name="isBuiltin">The handle is a builtin handle managed by RenderGraph internally.</param>
/// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns>
internal TextureHandle ImportTexture(RTHandle rt, bool isBuiltin)
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportTexture(rt, isBuiltin);
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="rt">Backbuffer render target identifier.</param>
/// <param name="info">The properties of the passed in RTHandle.</param>
/// <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>
/// <returns>A new TextureHandle that represents the imported texture in the context of this rendergraph.</returns>
public TextureHandle ImportBackbuffer(RenderTargetIdentifier rt, RenderTargetInfo info, ImportResourceParams importParams = new ImportResourceParams())
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportBackbuffer(rt, info, importParams);
}
/// <summary>
/// Create a new Render Graph Texture resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="desc">Texture descriptor.</param>
/// <returns>A new TextureHandle.</returns>
public TextureHandle CreateTexture(in TextureDesc desc)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateTexture(desc);
}
/// <summary>
/// Create a new Render Graph Texture resource using the descriptor from another texture.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="texture">Texture from which the descriptor should be used.</param>
/// <returns>A new TextureHandle.</returns>
public TextureHandle CreateTexture(TextureHandle texture)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateTexture(in m_Resources.GetTextureResourceDesc(texture.handle));
}
/// <summary>
/// Create a new Render Graph Texture resource using the descriptor from another texture.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="texture">Texture from which the descriptor should be used.</param>
/// <param name="name">The destination texture name.</param>
/// <param name="clear">Texture needs to be cleared on first use.</param>
/// <returns>A new TextureHandle.</returns>
public TextureHandle CreateTexture(TextureHandle texture, string name, bool clear = false)
{
CheckNotUsedWhenExecuting();
var destinationDesc = GetTextureDesc(texture);
destinationDesc.name = name;
destinationDesc.clearBuffer = clear;
return m_Resources.CreateTexture(destinationDesc);
}
/// <summary>
/// Create a new Render Graph Texture if the passed handle is invalid and use said handle as output.
/// If the passed handle is valid, no texture is created.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="desc">Desc used to create the texture.</param>
/// <param name="texture">Texture from which the descriptor should be used.</param>
public void CreateTextureIfInvalid(in TextureDesc desc, ref TextureHandle texture)
{
CheckNotUsedWhenExecuting();
if (!texture.IsValid())
texture = m_Resources.CreateTexture(desc);
}
/// <summary>
/// Gets the descriptor of the specified Texture resource.
/// </summary>
/// <param name="texture">Texture resource from which the descriptor is requested.</param>
/// <returns>The input texture descriptor.</returns>
public TextureDesc GetTextureDesc(in TextureHandle texture)
{
return m_Resources.GetTextureResourceDesc(texture.handle);
}
/// <summary>
/// Gets the descriptor of the specified Texture resource.
/// </summary>
/// <param name="texture">Texture resource from which the descriptor is requested.</param>
/// <returns>The input texture descriptor.</returns>
public RenderTargetInfo GetRenderTargetInfo(TextureHandle texture)
{
RenderTargetInfo info;
m_Resources.GetRenderTargetInfo(texture.handle, out info);
return info;
}
/// <summary>
/// Creates a new Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="desc">Renderer List descriptor.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateRendererList(in CoreRendererListDesc desc)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateRendererList(desc);
}
/// <summary>
/// Creates a new Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="desc">Renderer List descriptor.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateRendererList(in RendererListParams desc)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateRendererList(desc);
}
/// <summary>
/// Creates a new Shadow Renderer List Render Graph resource.
/// </summary>
/// <param name="shadowDrawingSettings">DrawSettings that describe the shadow drawcall.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateShadowRendererList(ref ShadowDrawingSettings shadowDrawingSettings)
{
return m_Resources.CreateShadowRendererList(m_RenderGraphContext.renderContext, ref shadowDrawingSettings);
}
/// <summary>
/// Creates a new Gizmo Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the Gizmo.</param>
/// <param name="gizmoSubset">GizmoSubset that specifies whether gizmos render before or after postprocessing for a camera render. </param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateGizmoRendererList(in Camera camera, in GizmoSubset gizmoSubset)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateGizmoRendererList(m_RenderGraphContext.renderContext, camera, gizmoSubset);
}
/// <summary>
/// Creates a new UIOverlay Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the full UIOverlay.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateUIOverlayRendererList(in Camera camera)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateUIOverlayRendererList(m_RenderGraphContext.renderContext, camera, UISubset.All);
}
/// <summary>
/// Creates a new UIOverlay Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering some subset of the UIOverlay.</param>
/// <param name="uiSubset">Enum flag that specifies which subset to render.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateUIOverlayRendererList(in Camera camera, in UISubset uiSubset)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateUIOverlayRendererList(m_RenderGraphContext.renderContext, camera, uiSubset);
}
/// <summary>
/// Creates a new WireOverlay Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the WireOverlay.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateWireOverlayRendererList(in Camera camera)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateWireOverlayRendererList(m_RenderGraphContext.renderContext, camera);
}
/// <summary>
/// Creates a new Skybox Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the Skybox.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateSkyboxRendererList(in Camera camera)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera);
}
/// <summary>
/// Creates a new Skybox Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the Skybox.</param>
/// <param name="projectionMatrix">The projection matrix used during XR rendering of the skybox.</param>
/// <param name="viewMatrix">The view matrix used during XR rendering of the skybox.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateSkyboxRendererList(in Camera camera, Matrix4x4 projectionMatrix, Matrix4x4 viewMatrix)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera, projectionMatrix, viewMatrix);
}
/// <summary>
/// Creates a new Skybox Renderer List Render Graph resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="camera">The camera that is used for rendering the Skybox.</param>
/// <param name="projectionMatrixL">The left eye projection matrix used during Legacy single pass XR rendering of the skybox.</param>
/// <param name="viewMatrixL">The left eye view matrix used during Legacy single pass XR rendering of the skybox.</param>
/// <param name="projectionMatrixR">The right eye projection matrix used during Legacy single pass XR rendering of the skybox.</param>
/// <param name="viewMatrixR">The right eye view matrix used during Legacy single pass XR rendering of the skybox.</param>
/// <returns>A new RendererListHandle.</returns>
public RendererListHandle CreateSkyboxRendererList(in Camera camera, Matrix4x4 projectionMatrixL, Matrix4x4 viewMatrixL, Matrix4x4 projectionMatrixR, Matrix4x4 viewMatrixR)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateSkyboxRendererList(m_RenderGraphContext.renderContext, camera, projectionMatrixL, viewMatrixL, projectionMatrixR, viewMatrixR);
}
/// <summary>
/// Import an external Graphics Buffer to the Render Graph.
/// Any pass writing to an imported graphics buffer will be considered having side effects and can't be automatically culled.
/// </summary>
/// <param name="graphicsBuffer">External Graphics Buffer that needs to be imported.</param>
/// <returns>A new GraphicsBufferHandle.</returns>
public BufferHandle ImportBuffer(GraphicsBuffer graphicsBuffer)
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportBuffer(graphicsBuffer);
}
/// <summary>
/// Create a new Render Graph Graphics Buffer resource.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="desc">Graphics Buffer descriptor.</param>
/// <returns>A new GraphicsBufferHandle.</returns>
public BufferHandle CreateBuffer(in BufferDesc desc)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateBuffer(desc);
}
/// <summary>
/// Create a new Render Graph Graphics Buffer resource using the descriptor from another graphics buffer.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="graphicsBuffer">Graphics Buffer from which the descriptor should be used.</param>
/// <returns>A new GraphicsBufferHandle.</returns>
public BufferHandle CreateBuffer(in BufferHandle graphicsBuffer)
{
CheckNotUsedWhenExecuting();
return m_Resources.CreateBuffer(in m_Resources.GetBufferResourceDesc(graphicsBuffer.handle));
}
/// <summary>
/// Gets the descriptor of the specified Graphics Buffer resource.
/// </summary>
/// <param name="graphicsBuffer">Graphics Buffer resource from which the descriptor is requested.</param>
/// <returns>The input graphics buffer descriptor.</returns>
public BufferDesc GetBufferDesc(in BufferHandle graphicsBuffer)
{
return m_Resources.GetBufferResourceDesc(graphicsBuffer.handle);
}
/// <summary>
/// Import an external RayTracingAccelerationStructure to the Render Graph.
/// Any pass writing to (building) an imported RayTracingAccelerationStructure will be considered having side effects and can't be automatically culled.
/// </summary>
/// <remarks>
/// This API cannot be called during the Render Graph execution, please call it outside of SetRenderFunc().
/// </remarks>
/// <param name="accelStruct">External RayTracingAccelerationStructure that needs to be imported.</param>
/// <param name="name">Optional name for identifying the RayTracingAccelerationStructure in the Render Graph.</param>
/// <returns>A new RayTracingAccelerationStructureHandle.</returns>
public RayTracingAccelerationStructureHandle ImportRayTracingAccelerationStructure(in RayTracingAccelerationStructure accelStruct, string name = null)
{
CheckNotUsedWhenExecuting();
return m_Resources.ImportRayTracingAccelerationStructure(accelStruct, name);
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenExecuting()
{
if (enableValidityChecks && m_RenderGraphState == RenderGraphState.Executing)
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.Executing));
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenRecordingGraph()
{
if (enableValidityChecks && m_RenderGraphState == RenderGraphState.RecordingGraph)
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.RecordingGraph));
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenRecordPassOrExecute()
{
if (enableValidityChecks && (m_RenderGraphState == RenderGraphState.RecordingPass || m_RenderGraphState == RenderGraphState.Executing))
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.RecordingPass | RenderGraphState.Executing));
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenRecordingPass()
{
if (enableValidityChecks && m_RenderGraphState == RenderGraphState.RecordingPass)
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.RecordingPass));
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenActive()
{
if (enableValidityChecks && (m_RenderGraphState & RenderGraphState.Active) != RenderGraphState.Idle)
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.Active));
}
[Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")]
void CheckNotUsedWhenIdle()
{
if (enableValidityChecks && m_RenderGraphState == RenderGraphState.Idle)
throw new InvalidOperationException(RenderGraphExceptionMessages.GetExceptionMessage(RenderGraphState.Active));
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph records a pass, please call it within SetRenderFunc() or outside of AddUnsafePass()/AddComputePass()/AddRasterRenderPass().
/// </remarks>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns>
public IRasterRenderGraphBuilder AddRasterRenderPass<PassData>(string passName, out PassData passData
#if !CORE_PACKAGE_DOCTOOLS
, [CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
return AddRasterRenderPass(passName, out passData, GetDefaultProfilingSampler(passName), file, line);
}
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <param name="sampler">Profiling sampler used around the pass.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns>
public IRasterRenderGraphBuilder AddRasterRenderPass<PassData>(string passName, out PassData passData, ProfilingSampler sampler
#if !CORE_PACKAGE_DOCTOOLS
,[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
CheckNotUsedWhenRecordingPass();
m_RenderGraphState = RenderGraphState.RecordingPass;
var renderPass = m_RenderGraphPool.Get<RasterRenderGraphPass<PassData>>();
renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Raster, sampler);
AddPassDebugMetadata(renderPass, file, line);
passData = renderPass.data;
m_RenderPasses.Add(renderPass);
m_builderInstance.Setup(renderPass, m_Resources, this);
return m_builderInstance;
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph records a pass, please call it within SetRenderFunc() or outside of AddUnsafePass()/AddComputePass()/AddRasterRenderPass().
/// </remarks>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IRasterRenderGraphBuilder used to setup the new Rasterization Render Pass.</returns>
public IComputeRenderGraphBuilder AddComputePass<PassData>(string passName, out PassData passData
#if !CORE_PACKAGE_DOCTOOLS
, [CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
return AddComputePass(passName, out passData, GetDefaultProfilingSampler(passName), file, line);
}
/// <summary>
/// Add a new Compute Render Pass to the Render Graph. Compute passes can execute compute workloads but cannot do rasterization.
/// </summary>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <param name="sampler">Profiling sampler used around the pass.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IComputeRenderGraphBuilder used to setup the new Compute Render Pass.</returns>
public IComputeRenderGraphBuilder AddComputePass<PassData>(string passName, out PassData passData, ProfilingSampler sampler
#if !CORE_PACKAGE_DOCTOOLS
,[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
CheckNotUsedWhenRecordingPass();
m_RenderGraphState = RenderGraphState.RecordingPass;
var renderPass = m_RenderGraphPool.Get<ComputeRenderGraphPass<PassData>>();
renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Compute, sampler);
AddPassDebugMetadata(renderPass, file, line);
passData = renderPass.data;
m_RenderPasses.Add(renderPass);
m_builderInstance.Setup(renderPass, m_Resources, this);
return m_builderInstance;
}
/// <summary>
/// Add a new Unsafe Render Pass to the Render Graph. Unsafe passes can do certain operations compute/raster render passes cannot do and have
/// access to the full command buffer API. The unsafe API should be used sparingly as it has the following downsides:
/// - Limited automatic validation of the commands and resource dependencies. The user is responsible to ensure that all dependencies are correctly declared.
/// - All native render passes will be serialized out.
/// - In the future the render graph compiler may generate a sub-optimal command stream for unsafe passes.
/// When using a unsafe pass the graph will also not automatically set up graphics state like rendertargets. The pass should do this itself
/// using cmd.SetRenderTarget and related commands.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph records a pass, please call it within SetRenderFunc() or outside of AddUnsafePass()/AddComputePass()/AddRasterRenderPass().
/// </remarks>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IUnsafeRenderGraphBuilder used to setup the new Unsafe Render Pass.</returns>
public IUnsafeRenderGraphBuilder AddUnsafePass<PassData>(string passName, out PassData passData
#if !CORE_PACKAGE_DOCTOOLS
, [CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
return AddUnsafePass(passName, out passData, GetDefaultProfilingSampler(passName), file, line);
}
/// <summary>
/// Add a new unsafe Render Pass to the Render Graph. Unsafe passes can do certain operations compute/raster render passes cannot do and have
/// access to the full command buffer API. The unsafe API should be used sparingly as it has the following downsides:
/// - Limited automatic validation of the commands and resource dependencies. The user is responsible to ensure that all dependencies are correctly declared.
/// - All native render passes will be serialized out.
/// - In the future the render graph compiler may generate a sub-optimal command stream for unsafe passes.
/// When using an unsafe pass the graph will also not automatically set up graphics state like rendertargets. The pass should do this itself
/// using cmd.SetRenderTarget and related commands.
/// </summary>
/// <typeparam name="PassData">Type of the class to use to provide data to the Render Pass.</typeparam>
/// <param name="passName">Name of the new Render Pass (this is also be used to generate a GPU profiling marker).</param>
/// <param name="passData">Instance of PassData that is passed to the render function and you must fill.</param>
/// <param name="sampler">Profiling sampler used around the pass.</param>
/// <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>
/// <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>
/// <returns>A new instance of a IUnsafeRenderGraphBuilder used to setup the new unsafe Render Pass.</returns>
public IUnsafeRenderGraphBuilder AddUnsafePass<PassData>(string passName, out PassData passData, ProfilingSampler sampler
#if !CORE_PACKAGE_DOCTOOLS
, [CallerFilePath] string file = "",
[CallerLineNumber] int line = 0) where PassData : class, new()
#endif
{
CheckNotUsedWhenRecordingPass();
m_RenderGraphState = RenderGraphState.RecordingPass;
var renderPass = m_RenderGraphPool.Get<UnsafeRenderGraphPass<PassData>>();
renderPass.Initialize(m_RenderPasses.Count, m_RenderGraphPool.Get<PassData>(), passName, RenderGraphPassType.Unsafe, sampler);
renderPass.AllowGlobalState(true);
AddPassDebugMetadata(renderPass, file, line);
passData = renderPass.data;
m_RenderPasses.Add(renderPass);
m_builderInstance.Setup(renderPass, m_Resources, this);
return m_builderInstance;
}
/// <summary>
/// Starts the recording of the render graph.
/// This must be called before adding any pass to the render graph.
/// </summary>
/// <remarks>
/// This API cannot be called when Render Graph is active, please call it outside of RecordRenderGraph().
/// </remarks>
/// <param name="parameters">Parameters necessary for the render graph execution.</param>
/// <example>
/// <para>Begin recording the Render Graph.</para>
/// <code>
/// renderGraph.BeginRecording(parameters)
/// // Add your render graph passes here.
/// renderGraph.EndRecordingAndExecute()
/// </code>
/// </example>
public void BeginRecording(in RenderGraphParameters parameters)
{
CheckNotUsedWhenActive();
m_ExecutionExceptionWasRaised = false;
m_RenderGraphState = RenderGraphState.RecordingGraph;
m_CurrentFrameIndex = parameters.currentFrameIndex;
m_CurrentExecutionId = parameters.executionId;
// Ignore preview cameras and render requests for debug data generation. They would cause the same camera to
// be rendered twice with different render graphs, confusing users and causing the RG Viewer to constantly update.
m_CurrentExecutionCanGenerateDebugData = parameters.generateDebugData && parameters.executionId != EntityId.None;
m_renderTextureUVOriginStrategy = parameters.renderTextureUVOriginStrategy;
m_Resources.BeginRenderGraph(m_ExecutionCount++);
m_DefaultResources.InitializeForRendering(this);
m_RenderGraphContext.cmd = parameters.commandBuffer;
m_RenderGraphContext.renderContext = parameters.scriptableRenderContext;
m_RenderGraphContext.contextlessTesting = parameters.invalidContextForTesting;
m_RenderGraphContext.renderGraphPool = m_RenderGraphPool;
m_RenderGraphContext.defaultResources = m_DefaultResources;
// With the actual implementation of the Frame Debugger, we cannot re-use resources during the same frame
// or it breaks the rendering of the pass preview, since the FD copies the texture after the execution of the RG.
m_RenderGraphContext.forceResourceCreation =
#if UNITY_EDITOR || DEVELOPMENT_BUILD
FrameDebugger.enabled;
#else
false;
#endif
}
/// <summary>
/// Ends the recording and executes the render graph.
/// This must be called once all passes have been added to the render graph.
/// </summary>
public void EndRecordingAndExecute()
{
CheckNotUsedWhenRecordPassOrExecute();
Execute();
ClearCompiledGraph();
m_Resources.EndExecute();
InvalidateContext();
m_RenderGraphState = RenderGraphState.Idle;
}
/// <summary>
/// Catches and logs exceptions that could happen during the graph recording or execution.
/// </summary>
/// <param name="e">The exception thrown by the graph.</param>
/// <returns>True if contexless testing is enabled, false otherwise.</returns>
public bool ResetGraphAndLogException(Exception e)
{
m_RenderGraphState = RenderGraphState.Idle;
if (!m_RenderGraphContext.contextlessTesting)
{
// If we're not testing log the exception and swallow it.
// TODO: Do we really want to swallow exceptions here? Not a very c# thing to do.
Debug.LogError(RenderGraphExceptionMessages.k_RenderGraphExecutionError);
if (!m_ExecutionExceptionWasRaised) // Already logged. TODO: There is probably a better way in C# to handle that.
{
Debug.LogException(e);
}
m_ExecutionExceptionWasRaised = true;
}
CommandBuffer.ThrowOnSetRenderTarget = false;
CleanupResourcesAndGraph();
// If there has been an error, we have to flush the command being built along the RG data structure,
// because otherwise the command might try to use resources we just deleted.
m_RenderGraphContext.cmd.Clear();
return m_RenderGraphContext.contextlessTesting;
}
/// <summary>
/// Execute the Render Graph in its current state.
/// </summary>
internal void Execute()
{
m_ExecutionExceptionWasRaised = false;
m_RenderGraphState = RenderGraphState.Executing;
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (m_RenderGraphContext.cmd == null)
throw new InvalidOperationException("RenderGraph.BeginRecording was not called before executing the render graph.");
ClearCacheIfNewActiveDebugSession();
#endif
int graphHash = m_EnableCompilationCaching ? ComputeGraphHash() : 0;
CompileNativeRenderGraph(graphHash);
// Must be set after compilation when the compiler has been initialized
m_RenderGraphContext.compilerContext = nativeCompiler?.contextData;
m_Resources.BeginExecute(m_CurrentFrameIndex);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
// Feeding Render Graph Viewer before resource deallocation at pass execution
GenerateDebugData(graphHash);
#endif
ExecuteNativeRenderGraph();
// Clear the shader bindings for all global textures to make sure bindings don't leak outside the graph
ClearGlobalBindings();
}
class ProfilingScopePassData
{
public ProfilingSampler sampler;
}
const string k_BeginProfilingSamplerPassName = "BeginProfile";
const string k_EndProfilingSamplerPassName = "EndProfile";
/// <summary>
/// Begin a profiling scope.
/// </summary>
/// <param name="sampler">Sampler used for profiling.</param>
/// <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>
/// <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>
public void BeginProfilingSampler(ProfilingSampler sampler,
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
{
if (sampler == null)
return;
using (var builder = AddUnsafePass<ProfilingScopePassData>(k_BeginProfilingSamplerPassName, out var passData, (ProfilingSampler)null, file, line))
{
passData.sampler = sampler;
builder.AllowPassCulling(false);
builder.GenerateDebugData(false);
builder.SetRenderFunc(static (ProfilingScopePassData data, UnsafeGraphContext ctx) =>
{
data.sampler.Begin(CommandBufferHelpers.GetNativeCommandBuffer(ctx.cmd));
});
}
}
/// <summary>
/// End a profiling scope.
/// </summary>
/// <param name="sampler">Sampler used for profiling.</param>
/// <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>
/// <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>
public void EndProfilingSampler(ProfilingSampler sampler,
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
{
if (sampler == null)
return;
using (var builder = AddUnsafePass<ProfilingScopePassData>(k_EndProfilingSamplerPassName, out var passData, (ProfilingSampler)null, file, line))
{
passData.sampler = sampler;
builder.AllowPassCulling(false);
builder.GenerateDebugData(false);
builder.SetRenderFunc(static (ProfilingScopePassData data, UnsafeGraphContext ctx) =>
{
data.sampler.End(CommandBufferHelpers.GetNativeCommandBuffer(ctx.cmd));
});
}
}
#endregion
#region Internal Interface
// Internal for testing purpose only
internal void ClearCurrentCompiledGraph()
{
ClearCompiledGraph();
}
void ClearCompiledGraph()
{
ClearRenderPasses();
m_Resources.Clear(m_ExecutionExceptionWasRaised);
m_RendererLists.Clear();
registeredGlobals.Clear();
}
void InvalidateContext()
{
m_RenderGraphContext.cmd = null;
m_RenderGraphContext.renderGraphPool = null;
m_RenderGraphContext.defaultResources = null;
m_RenderGraphContext.compilerContext = null;
}
internal delegate void OnGraphRegisteredDelegate(string graphName);
internal static event OnGraphRegisteredDelegate onGraphRegistered;
internal static event OnGraphRegisteredDelegate onGraphUnregistered;
internal delegate void OnExecutionRegisteredDelegate(string graphName, EntityId executionId, string executionName);
internal static event OnExecutionRegisteredDelegate onExecutionRegistered;
#endregion
#region Private Interface
// Internal for testing purpose only.
internal int ComputeGraphHash()
{
using (new ProfilingScope(ProfilingSampler.Get(RenderGraphProfileId.ComputeHashRenderGraph)))
{
var hash128 = HashFNV1A32.Create();
for (int i = 0; i < m_RenderPasses.Count; ++i)
m_RenderPasses[i].ComputeHash(ref hash128, m_Resources);
return hash128.value;
}
}
internal bool GetImportedFallback(TextureDesc desc, out TextureHandle fallback)
{
fallback = TextureHandle.nullHandle;
// We don't have any fallback texture with MSAA
if (!desc.bindTextureMS)
{
if (desc.depthBufferBits != DepthBits.None)
{
fallback = defaultResources.whiteTexture;
}
else if (desc.clearColor == Color.black || desc.clearColor == default)
{
if (desc.dimension == TextureXR.dimension)
fallback = defaultResources.blackTextureXR;
else if (desc.dimension == TextureDimension.Tex3D)
fallback = defaultResources.blackTexture3DXR;
else if (desc.dimension == TextureDimension.Tex2D)
fallback = defaultResources.blackTexture;
}
else if (desc.clearColor == Color.white)
{
if (desc.dimension == TextureXR.dimension)
fallback = defaultResources.whiteTextureXR;
else if (desc.dimension == TextureDimension.Tex2D)
fallback = defaultResources.whiteTexture;
}
}
return fallback.IsValid();
}
void ClearRenderPasses()
{
foreach (var pass in m_RenderPasses)
pass.Release(m_RenderGraphPool);
m_RenderPasses.Clear();
}
ProfilingSampler GetDefaultProfilingSampler(string name)
{
// In non-dev builds, ProfilingSampler.Get returns null, so we'd always end up executing this.
// To avoid that we also ifdef the code out here.
#if DEVELOPMENT_BUILD || UNITY_EDITOR
int hash = name.GetHashCode();
if (!m_DefaultProfilingSamplers.TryGetValue(hash, out var sampler))
{
sampler = new ProfilingSampler(name);
m_DefaultProfilingSamplers.Add(hash, sampler);
}
return sampler;
#else
return null;
#endif
}
// Register the graph in the RenderGraph Viewer.
// Registered once on graph creation, indicates to the viewer that this render graph exists.
void RegisterGraph()
{
s_RegisteredExecutions.Add(this, new List<DebugExecutionItem>());
onGraphRegistered?.Invoke(this.name);
}
// Unregister the graph from the render graph viewer.
// Should only be unregistered when it is destroyed, not before.
// Should not be called when an error happens because the existence of the graph doesn't depend on its state
// or whether it's been cleaned.
void UnregisterGraph()
{
s_RegisteredExecutions.Remove(this);
onGraphUnregistered?.Invoke(name);
}
// Note: obj.name allocates so make sure you only call this when debug tools / options are active
static string GetExecutionNameAllocates(EntityId entityId)
{
var obj = Resources.EntityIdToObject(entityId);
return obj != null ? obj.name : $"RenderGraphExecution ({entityId})";
}
static bool s_DebugSessionWasActive;
void ClearCacheIfNewActiveDebugSession()
{
if (RenderGraphDebugSession.hasActiveDebugSession && !s_DebugSessionWasActive)
{
// Invalidate cache whenever a debug session becomes active. This is because while the DebugSession is
// inactive, certain debug data (store/load/pass break audits) stored inside the compilation cache is
// not getting generated. Therefore we clear compilation cache to regenerate this debug data.
// This needs to be done before the compilation step.
m_CompilationCache?.Clear();
}
s_DebugSessionWasActive = RenderGraphDebugSession.hasActiveDebugSession;
}
void GenerateDebugData(int graphHash)
{
if (!RenderGraphDebugSession.hasActiveDebugSession || !m_CurrentExecutionCanGenerateDebugData || m_ExecutionExceptionWasRaised)
return;
var registeredExecutions = s_RegisteredExecutions[this];
// The reverse loop prunes deleted cameras and checks if the current camera needs to be registered.
bool alreadyRegistered = false;
using var deletedExecutionIdsDisposable = ListPool<EntityId>.Get(out var deletedExecutionIds);
for (int i = registeredExecutions.Count - 1; i >= 0; i--)
{
DebugExecutionItem executionItem = registeredExecutions[i];
if (Resources.EntityIdToObject(executionItem.id) == null)
{
registeredExecutions.RemoveAt(i);
deletedExecutionIds.Add(executionItem.id);
continue;
}
if (executionItem.id == m_CurrentExecutionId)
alreadyRegistered = true;
}
var currentExecutionName = GetExecutionNameAllocates(m_CurrentExecutionId);
if (!alreadyRegistered)
{
registeredExecutions.Add(new DebugExecutionItem(m_CurrentExecutionId, currentExecutionName));
}
if (deletedExecutionIds.Count > 0)
RenderGraphDebugSession.DeleteExecutionIds(name, deletedExecutionIds); // Clear stale debug data entries for deleted cameras
if (!alreadyRegistered)
{
onExecutionRegistered?.Invoke(name, m_CurrentExecutionId, currentExecutionName);
}
// When compilation caching is disabled, we don't hash the graph. In order to avoid UI needing to update every
// frame, we still want to use the hash to know if debug data should be updated, so compute the hash now.
if (!m_EnableCompilationCaching)
graphHash = ComputeGraphHash();
var debugData = RenderGraphDebugSession.GetDebugData(name, m_CurrentExecutionId);
bool cameraWasRenamed = debugData.executionName != currentExecutionName;
if (debugData.valid && debugData.graphHash == graphHash && !cameraWasRenamed)
return; // No need to update
debugData.Clear();
debugData.executionName = currentExecutionName;
debugData.graphHash = graphHash;
nativeCompiler.GenerateNativeCompilerDebugData(ref debugData);
debugData.valid = true;
RenderGraphDebugSession.SetDebugData(name, m_CurrentExecutionId, debugData);
}
#endregion
Dictionary<int, TextureHandle> registeredGlobals = new Dictionary<int, TextureHandle>();
internal void SetGlobal(in TextureHandle h, int globalPropertyId)
{
if (!h.IsValid())
throw new ArgumentException("Attempting to register an invalid texture handle as a global");
registeredGlobals[globalPropertyId] = h;
}
internal bool IsGlobal(int globalPropertyId)
{
return registeredGlobals.ContainsKey(globalPropertyId);
}
internal Dictionary<int, TextureHandle>.ValueCollection AllGlobals()
{
return registeredGlobals.Values;
}
internal TextureHandle GetGlobal(int globalPropertyId)
{
TextureHandle h;
registeredGlobals.TryGetValue(globalPropertyId, out h);
return h;
}
/// <summary>
/// Clears the shader bindings associated with the registered globals in the graph
///
/// This prevents later rendering logic from accidentally relying on stale shader bindings that were set
/// earlier during graph execution.
/// </summary>
internal void ClearGlobalBindings()
{
// Set all the global texture shader bindings to the default black texture.
// This doesn't technically "clear" the shader bindings, but it's the closest we can do.
foreach (var globalTex in registeredGlobals)
{
m_RenderGraphContext.cmd.SetGlobalTexture(globalTex.Key, defaultResources.blackTexture);
}
}
}
/// <summary>
/// Render Graph Scoped Profiling markers
/// </summary>
[MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
public struct RenderGraphProfilingScope : IDisposable
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
ProfilingSampler m_Sampler;
RenderGraph m_RenderGraph;
bool m_Disposed;
#endif
/// <summary>
/// Profiling Scope constructor
/// </summary>
/// <param name="renderGraph">Render Graph used for this scope.</param>
/// <param name="sampler">Profiling Sampler to be used for this scope.</param>
public RenderGraphProfilingScope(RenderGraph renderGraph, ProfilingSampler sampler)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
m_RenderGraph = renderGraph;
m_Sampler = sampler;
m_Disposed = false;
renderGraph.BeginProfilingSampler(sampler);
#endif
}
/// <summary>
/// Dispose pattern implementation
/// </summary>
public void Dispose()
{
Dispose(true);
}
// Protected implementation of Dispose pattern.
void Dispose(bool disposing)
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
if (m_Disposed)
return;
// As this is a struct, it could have been initialized using an empty constructor so we
// need to make sure `cmd` isn't null to avoid a crash. Switching to a class would fix
// this but will generate garbage on every frame (and this struct is used quite a lot).
if (disposing)
{
m_RenderGraph.EndProfilingSampler(m_Sampler);
}
m_Disposed = true;
#endif
}
}
}
#endif