using System; using System.Collections.Generic; using UnityEngine.Assertions; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; namespace UnityEngine.Rendering.HighDefinition { /// Called when the rendering has completed. /// A command buffer that can be used. /// The buffers that has been requested. /// Several properties that were computed for this frame. public delegate void FramePassCallback(CommandBuffer cmd, List buffers, RenderOutputProperties outputProperties); /// /// Called to allocate a RTHandle for a specific AOVBuffer. /// /// The AOVBuffer to allocatE. public delegate RTHandle AOVRequestBufferAllocator(AOVBuffers aovBufferId); /// Called when the rendering has completed. /// A command buffer that can be used. /// The buffers that has been requested. /// Several properties that were computed for this frame. public delegate void FramePassCallbackEx(CommandBuffer cmd, List buffers, List customPassbuffers, RenderOutputProperties outputProperties); /// /// Called to allocate a RTHandle for a specific custom pass AOVBuffer. /// /// The AOVBuffer to allocatE. public delegate RTHandle AOVRequestCustomPassBufferAllocator(CustomPassAOVBuffers aovBufferId); /// Describes a frame pass. public struct AOVRequestData { /// Default frame pass settings. [Obsolete("Since 2019.3, use AOVRequestData.NewDefault() instead.")] public static readonly AOVRequestData @default = default; /// /// Instantiate a new AOV request data with default values. /// /// Note: Allocates memory by the garbage collector. /// If you intend only to read the default values, you should use . /// /// A new AOV request data with default values. public static AOVRequestData NewDefault() => new AOVRequestData { m_Settings = AOVRequest.NewDefault(), m_RequestedAOVBuffers = new AOVBuffers[] { }, m_Callback = null }; /// Default frame pass settings. public static readonly AOVRequestData defaultAOVRequestDataNonAlloc = NewDefault(); private AOVRequest m_Settings; private AOVBuffers[] m_RequestedAOVBuffers; private CustomPassAOVBuffers[] m_CustomPassAOVBuffers; private FramePassCallback m_Callback; private FramePassCallbackEx m_CallbackEx; private readonly AOVRequestBufferAllocator m_BufferAllocator; private readonly AOVRequestCustomPassBufferAllocator m_CustomPassBufferAllocator; private List m_LightFilter; /// Whether this frame pass is valid. public bool isValid => (m_RequestedAOVBuffers != null || m_CustomPassAOVBuffers != null) && (m_Callback != null || m_CallbackEx != null); /// Whether internal rendering should be done at the same format as the user allocated AOV output buffer. public bool overrideRenderFormat => m_Settings.overrideRenderFormat; /// Create a new frame pass. /// Settings to use. /// Buffer allocators to use. /// If null, all light will be rendered, if not, only those light will be rendered. /// The requested buffers for the callback. /// The callback to execute. public AOVRequestData( AOVRequest settings, AOVRequestBufferAllocator bufferAllocator, List lightFilter, AOVBuffers[] requestedAOVBuffers, FramePassCallback callback ) { m_Settings = settings; m_BufferAllocator = bufferAllocator; m_RequestedAOVBuffers = requestedAOVBuffers; m_LightFilter = lightFilter; m_Callback = callback; m_CallbackEx = null; m_CustomPassAOVBuffers = null; m_CustomPassBufferAllocator = null; } /// Create a new frame pass. /// Settings to use. /// Buffer allocators to use. /// If null, all light will be rendered, if not, only those light will be rendered. /// The requested buffers for the callback. /// The custom pass buffers that will be captured. /// Buffer allocators to use for custom passes. /// The callback to execute. public AOVRequestData( AOVRequest settings, AOVRequestBufferAllocator bufferAllocator, List lightFilter, AOVBuffers[] requestedAOVBuffers, CustomPassAOVBuffers[] customPassAOVBuffers, AOVRequestCustomPassBufferAllocator customPassBufferAllocator, FramePassCallbackEx callback ) { m_Settings = settings; m_BufferAllocator = bufferAllocator; m_RequestedAOVBuffers = requestedAOVBuffers; m_CustomPassAOVBuffers = customPassAOVBuffers; m_CustomPassBufferAllocator = customPassBufferAllocator; m_LightFilter = lightFilter; m_Callback = null; m_CallbackEx = callback; } /// Allocate texture if required. /// A buffer of texture ready to use. public void AllocateTargetTexturesIfRequired(ref List textures) { if (!isValid || textures == null) return; textures.Clear(); if (m_RequestedAOVBuffers != null) { foreach (var bufferId in m_RequestedAOVBuffers) textures.Add(m_BufferAllocator(bufferId)); } } /// Allocate texture if required. /// A buffer of textures ready to use. /// A buffer of textures ready to use for custom pass AOVs. public void AllocateTargetTexturesIfRequired(ref List textures, ref List customPassTextures) { if (!isValid || textures == null) return; textures.Clear(); customPassTextures.Clear(); if (m_RequestedAOVBuffers != null) { foreach (var bufferId in m_RequestedAOVBuffers) { var rtHandle = m_BufferAllocator(bufferId); textures.Add(rtHandle); if (rtHandle == null) { Debug.LogError("Allocation for requested AOVBuffers ID: " + bufferId.ToString() + " have fail. Please ensure the callback allocator do the correct allocation."); } } } if (m_CustomPassAOVBuffers != null) { foreach (var aovBufferId in m_CustomPassAOVBuffers) { var rtHandle = m_CustomPassBufferAllocator(aovBufferId); customPassTextures.Add(rtHandle); if (rtHandle == null) { Debug.LogError("Allocation for requested AOVBuffers ID: " + aovBufferId.ToString() + " have fail. Please ensure the callback for custom pass allocator do the correct allocation."); } } } } internal void OverrideBufferFormatForAOVs(ref GraphicsFormat format, List aovBuffers) { if (m_RequestedAOVBuffers == null || aovBuffers.Count == 0) { return; } var index = Array.IndexOf(m_RequestedAOVBuffers, AOVBuffers.Color); if (index < 0) { index = Array.IndexOf(m_RequestedAOVBuffers, AOVBuffers.Output); } if (index >= 0) { format = aovBuffers[index].rt.graphicsFormat; } } class PushCameraTexturePassData { public TextureHandle source; // Not super clean to not use TextureHandles here. In practice it's ok because those texture are never passed back to any other render pass. public RTHandle target; } internal void PushCameraTexture( RenderGraph renderGraph, AOVBuffers aovBufferId, HDCamera camera, TextureHandle source, List targets ) { if (!isValid || m_RequestedAOVBuffers == null) return; Assert.IsNotNull(m_RequestedAOVBuffers); Assert.IsNotNull(targets); var index = Array.IndexOf(m_RequestedAOVBuffers, aovBufferId); if (index == -1) return; using (var builder = renderGraph.AddRenderPass("Push AOV Camera Texture", out var passData, ProfilingSampler.Get(HDProfileId.AOVOutput + (int)aovBufferId))) { passData.source = builder.ReadTexture(source); passData.target = targets[index]; builder.SetRenderFunc( (PushCameraTexturePassData data, RenderGraphContext ctx) => { HDUtils.BlitCameraTexture(ctx.cmd, data.source, data.target); }); } } class PushCustomPassTexturePassData { public TextureHandle source; public RTHandle customPassSource; // Not super clean to not use TextureHandles here. In practice it's ok because those texture are never passed back to any other render pass. public RTHandle target; } internal void PushCustomPassTexture( RenderGraph renderGraph, CustomPassInjectionPoint injectionPoint, TextureHandle cameraSource, Lazy customPassSource, List targets ) { if (!isValid || m_CustomPassAOVBuffers == null) return; Assert.IsNotNull(targets); int index = -1; for (int i = 0; i < m_CustomPassAOVBuffers.Length; ++i) { if (m_CustomPassAOVBuffers[i].injectionPoint == injectionPoint) { index = i; break; } } if (index == -1) return; using (var builder = renderGraph.AddRenderPass("Push Custom Pass Texture", out var passData)) { if (m_CustomPassAOVBuffers[index].outputType == CustomPassAOVBuffers.OutputType.Camera) { passData.source = builder.ReadTexture(cameraSource); passData.customPassSource = null; } else { passData.customPassSource = customPassSource.Value; } passData.target = targets[index]; builder.SetRenderFunc( (PushCustomPassTexturePassData data, RenderGraphContext ctx) => { if (data.customPassSource != null) HDUtils.BlitCameraTexture(ctx.cmd, data.customPassSource, data.target); else HDUtils.BlitCameraTexture(ctx.cmd, data.source, data.target); }); } } /// Execute the frame pass callback. It assumes that the textures are properly initialized and filled. /// The command buffer to use. /// The textures to use. /// The properties computed for this frame. public void Execute(CommandBuffer cmd, List framePassTextures, RenderOutputProperties outputProperties) { if (!isValid) return; m_Callback(cmd, framePassTextures, outputProperties); } /// Execute the frame pass callback. It assumes that the textures are properly initialized and filled. /// The command buffer to use. /// The textures to use. /// The custom pass AOV textures to use. /// The properties computed for this frame. public void Execute(CommandBuffer cmd, List framePassTextures, List customPassTextures, RenderOutputProperties outputProperties) { if (!isValid) return; if (m_CallbackEx != null) { m_CallbackEx(cmd, framePassTextures, customPassTextures, outputProperties); } else { m_Callback(cmd, framePassTextures, outputProperties); } } /// Setup the display manager if necessary. /// public void SetupDebugData(ref DebugDisplaySettings debugDisplaySettings) { if (!isValid) return; debugDisplaySettings = new DebugDisplaySettings(); m_Settings.FillDebugData(debugDisplaySettings); } /// Whether a light should be rendered. /// The game object of the light to be rendered. /// true when the light must be rendered, false when it should be ignored. public bool IsLightEnabled(GameObject gameObject) => m_LightFilter == null || m_LightFilter.Contains(gameObject); internal bool hasLightFilter => m_LightFilter != null; internal int GetHash() { int hash = m_Settings.GetHashCode(); if (m_LightFilter != null) { foreach (var obj in m_LightFilter) { hash += obj.GetHashCode(); } } return hash; } internal bool HasSameSettings(AOVRequestData other) { if (m_Settings != other.m_Settings) return false; if (m_LightFilter != null) return m_LightFilter.Equals(other.m_LightFilter); return true; } } internal class AOVRequestDataComparer : IEqualityComparer { public bool Equals(AOVRequestData x, AOVRequestData y) { return x.HasSameSettings(y); } public int GetHashCode(AOVRequestData obj) { return obj.GetHash(); } } }