using System;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal.Internal
{
///
/// Copy the given color buffer to the given destination color buffer.
///
/// You can use this pass to copy a color buffer to the destination,
/// so you can use it later in rendering. For example, you can copy
/// the opaque texture to use it for distortion effects.
///
public class CopyColorPass : ScriptableRenderPass
{
int m_SampleOffsetShaderHandle;
Material m_SamplingMaterial;
Downsampling m_DownsamplingMethod;
Material m_CopyColorMaterial;
private RTHandle source { get; set; }
private RTHandle destination { get; set; }
private PassData m_PassData;
///
/// Creates a new CopyColorPass instance.
///
/// The RenderPassEvent to use.
/// The Material to use for downsampling quarter-resolution image with box filtering.
/// The Material to use for other downsampling options.
/// An optional custom profiling name to disambiguate multiple copy passes.
///
///
public CopyColorPass(RenderPassEvent evt, Material samplingMaterial, Material copyColorMaterial = null, string customPassName = null)
{
profilingSampler = customPassName != null ? new ProfilingSampler(customPassName) : ProfilingSampler.Get(URPProfileId.CopyColor);
m_PassData = new PassData();
m_SamplingMaterial = samplingMaterial;
m_CopyColorMaterial = copyColorMaterial;
m_SampleOffsetShaderHandle = Shader.PropertyToID("_SampleOffset");
renderPassEvent = evt;
m_DownsamplingMethod = Downsampling.None;
base.useNativeRenderPass = false;
}
///
/// Get a descriptor and filter mode for the required texture for this pass
///
///
///
///
///
///
///
public static void ConfigureDescriptor(Downsampling downsamplingMethod, ref RenderTextureDescriptor descriptor, out FilterMode filterMode)
{
descriptor.msaaSamples = 1;
descriptor.depthStencilFormat = GraphicsFormat.None;
if (downsamplingMethod == Downsampling._2xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 2);
descriptor.height = Mathf.Max(1, descriptor.height / 2);
}
else if (downsamplingMethod == Downsampling._4xBox || downsamplingMethod == Downsampling._4xBilinear)
{
descriptor.width = Mathf.Max(1, descriptor.width / 4);
descriptor.height = Mathf.Max(1, descriptor.height / 4);
}
filterMode = downsamplingMethod == Downsampling.None ? FilterMode.Point : FilterMode.Bilinear;
}
///
/// Configure the pass with the source and destination to execute on.
///
/// Source render target.
/// Destination render target.
/// The downsampling method to use.
[Obsolete("Use RTHandles for source and destination.", true)]
public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination, Downsampling downsampling)
{
throw new NotSupportedException("Setup with RenderTargetIdentifier has been deprecated. Use it with RTHandles instead.");
}
///
/// Configure the pass with the source and destination to execute on.
///
/// Source render target.
/// Destination render target.
/// The downsampling method to use.
public void Setup(RTHandle source, RTHandle destination, Downsampling downsampling)
{
this.source = source;
this.destination = destination;
m_DownsamplingMethod = downsampling;
}
///
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
cmd.SetGlobalTexture(destination.name, destination.nameID);
}
///
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
m_PassData.samplingMaterial = m_SamplingMaterial;
m_PassData.copyColorMaterial = m_CopyColorMaterial;
m_PassData.downsamplingMethod = m_DownsamplingMethod;
m_PassData.sampleOffsetShaderHandle = m_SampleOffsetShaderHandle;
var cmd = renderingData.commandBuffer;
// TODO RENDERGRAPH: Do we need a similar check in the RenderGraph path?
//It is possible that the given color target is now the frontbuffer
if (source == renderingData.cameraData.renderer.GetCameraColorFrontBuffer(cmd))
{
source = renderingData.cameraData.renderer.cameraColorTargetHandle;
}
#if ENABLE_VR && ENABLE_XR_MODULE
if (renderingData.cameraData.xr.supportsFoveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
#endif
ScriptableRenderer.SetRenderTarget(cmd, destination, k_CameraTarget, clearFlag, clearColor);
ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), m_PassData, source, renderingData.cameraData.xr.enabled);
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RTHandle source, bool useDrawProceduralBlit)
{
var samplingMaterial = passData.samplingMaterial;
var copyColorMaterial = passData.copyColorMaterial;
var downsamplingMethod = passData.downsamplingMethod;
var sampleOffsetShaderHandle = passData.sampleOffsetShaderHandle;
if (samplingMaterial == null)
{
Debug.LogErrorFormat(
"Missing {0}. Copy Color render pass will not execute. Check for missing reference in the renderer resources.",
samplingMaterial);
return;
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.CopyColor)))
{
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
switch (downsamplingMethod)
{
case Downsampling.None:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 0);
break;
case Downsampling._2xBilinear:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 1);
break;
case Downsampling._4xBox:
samplingMaterial.SetFloat(sampleOffsetShaderHandle, 2);
Blitter.BlitTexture(cmd, source, viewportScale, samplingMaterial, 0);
break;
case Downsampling._4xBilinear:
Blitter.BlitTexture(cmd, source, viewportScale, copyColorMaterial, 1);
break;
}
}
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
// internal RenderingData renderingData;
internal bool useProceduralBlit;
internal Material samplingMaterial;
internal Material copyColorMaterial;
internal Downsampling downsamplingMethod;
internal int sampleOffsetShaderHandle;
}
internal TextureHandle Render(RenderGraph renderGraph, ContextContainer frameData, out TextureHandle destination, in TextureHandle source, Downsampling downsampling)
{
m_DownsamplingMethod = downsampling;
UniversalCameraData cameraData = frameData.Get();
RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor;
ConfigureDescriptor(downsampling, ref descriptor, out var filterMode);
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, descriptor, "_CameraOpaqueTexture", true, filterMode);
RenderInternal(renderGraph, destination, source, cameraData.xr.enabled);
return destination;
}
// This will not create a new texture, but will reuse an existing one as destination.
// Typical use case is a persistent texture imported to the render graph. For example history textures.
// Note that the amount of downsampling is determined by the destination size.
// Therefore, the downsampling param controls only the algorithm (shader) used for the downsampling, not size.
internal void RenderToExistingTexture(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle destination, in TextureHandle source, Downsampling downsampling = Downsampling.None)
{
m_DownsamplingMethod = downsampling;
UniversalCameraData cameraData = frameData.Get();
RenderInternal(renderGraph, destination, source, cameraData.xr.enabled);
}
private void RenderInternal(RenderGraph renderGraph, in TextureHandle destination, in TextureHandle source, bool useProceduralBlit)
{
using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData, profilingSampler))
{
passData.destination = destination;
builder.SetRenderAttachment(destination, 0, AccessFlags.WriteAll);
passData.source = source;
builder.UseTexture(source, AccessFlags.Read);
passData.useProceduralBlit = useProceduralBlit;
passData.samplingMaterial = m_SamplingMaterial;
passData.copyColorMaterial = m_CopyColorMaterial;
passData.downsamplingMethod = m_DownsamplingMethod;
passData.sampleOffsetShaderHandle = m_SampleOffsetShaderHandle;
if (destination.IsValid())
builder.SetGlobalTextureAfterPass(destination, Shader.PropertyToID("_CameraOpaqueTexture"));
// TODO RENDERGRAPH: culling? force culling off for testing
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.source, data.useProceduralBlit);
});
}
}
}
}