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();
}
}
}