using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal.Internal
{
///
/// Copy the given depth buffer into the given destination depth buffer.
///
/// You can use this pass to copy a depth buffer to a destination,
/// so you can use it later in rendering. If the source texture has MSAA
/// enabled, the pass uses a custom MSAA resolve. If the source texture
/// does not have MSAA enabled, the pass uses a Blit or a Copy Texture
/// operation, depending on what the current platform supports.
///
public class CopyDepthPass : ScriptableRenderPass
{
private RTHandle source { get; set; }
private RTHandle destination { get; set; }
internal int MssaSamples { get; set; }
// In some cases (Scene view, XR and etc.) we actually want to output to depth buffer
// So this variable needs to be set to true to enable the correct copy shader semantic
internal bool CopyToDepth { get; set; }
// In XR CopyDepth, we need a special workaround to handle dummy color issue in RenderGraph.
internal bool CopyToDepthXR { get; set; }
// We need to know if we're copying to the backbuffer in order to handle y-flip correctly
internal bool CopyToBackbuffer { get; set; }
Material m_CopyDepthMaterial;
internal bool m_CopyResolvedDepth;
internal bool m_ShouldClear;
private PassData m_PassData;
///
/// Shader resource ids used to communicate with the shader implementation
///
static class ShaderConstants
{
public static readonly int _CameraDepthAttachment = Shader.PropertyToID("_CameraDepthAttachment");
public static readonly int _CameraDepthTexture = Shader.PropertyToID("_CameraDepthTexture");
public static readonly int _ZWriteShaderHandle = Shader.PropertyToID("_ZWrite");
}
///
/// Creates a new CopyDepthPass instance.
///
/// The RenderPassEvent to use.
/// The Shader to use for copying the depth.
/// Controls whether it should do a clear before copying the depth.
/// Controls whether it should do a copy to a depth format target.
/// Set to true if the source depth is MSAA resolved.
/// An optional custom profiling name to disambiguate multiple copy passes.
///
public CopyDepthPass(RenderPassEvent evt, Shader copyDepthShader, bool shouldClear = false, bool copyToDepth = false, bool copyResolvedDepth = false, string customPassName = null)
{
profilingSampler = customPassName != null ? new ProfilingSampler(customPassName) : ProfilingSampler.Get(URPProfileId.CopyDepth);
m_PassData = new PassData();
CopyToDepth = copyToDepth;
m_CopyDepthMaterial = copyDepthShader != null ? CoreUtils.CreateEngineMaterial(copyDepthShader) : null;
renderPassEvent = evt;
m_CopyResolvedDepth = copyResolvedDepth;
m_ShouldClear = shouldClear;
CopyToDepthXR = false;
CopyToBackbuffer = false;
}
///
/// Configure the pass with the source and destination to execute on.
///
/// Source Render Target
/// Destination Render Target
public void Setup(RTHandle source, RTHandle destination)
{
this.source = source;
this.destination = destination;
this.MssaSamples = -1;
}
///
/// Cleans up resources used by the pass.
///
public void Dispose()
{
CoreUtils.Destroy(m_CopyDepthMaterial);
}
///
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
// Disable obsolete warning for internal usage
#pragma warning disable CS0618
#if UNITY_EDITOR
// This is a temporary workaround for Editor as not setting any depth here
// would lead to overwriting depth in certain scenarios (reproducable while running DX11 tests)
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11)
ConfigureTarget(destination, destination);
else
#endif
ConfigureTarget(destination);
if (m_ShouldClear)
ConfigureClear(ClearFlag.All, Color.black);
#pragma warning restore CS0618
}
private class PassData
{
internal TextureHandle source;
internal UniversalCameraData cameraData;
internal Material copyDepthMaterial;
internal int msaaSamples;
internal bool copyResolvedDepth;
internal bool copyToDepth;
internal bool isDstBackbuffer;
}
///
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var cameraData = renderingData.frameData.Get();
m_PassData.copyDepthMaterial = m_CopyDepthMaterial;
m_PassData.msaaSamples = MssaSamples;
m_PassData.copyResolvedDepth = m_CopyResolvedDepth;
m_PassData.copyToDepth = CopyToDepth || CopyToDepthXR;
m_PassData.isDstBackbuffer = CopyToBackbuffer || CopyToDepthXR;
m_PassData.cameraData = cameraData;
var cmd = renderingData.commandBuffer;
cmd.SetGlobalTexture(ShaderConstants._CameraDepthAttachment, source.nameID);
#if ENABLE_VR && ENABLE_XR_MODULE
if (m_PassData.cameraData.xr.enabled)
{
if (m_PassData.cameraData.xr.supportsFoveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
}
#endif
ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), m_PassData, this.source);
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RTHandle source)
{
var copyDepthMaterial = passData.copyDepthMaterial;
var msaaSamples = passData.msaaSamples;
var copyResolvedDepth = passData.copyResolvedDepth;
var copyToDepth = passData.copyToDepth;
if (copyDepthMaterial == null)
{
Debug.LogErrorFormat("Missing {0}. Copy Depth render pass will not execute. Check for missing reference in the renderer resources.", copyDepthMaterial);
return;
}
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.CopyDepth)))
{
int cameraSamples = 0;
if (msaaSamples == -1)
{
RTHandle sourceTex = source;
cameraSamples = sourceTex.rt.antiAliasing;
}
else
cameraSamples = msaaSamples;
// When depth resolve is supported or multisampled texture is not supported, set camera samples to 1
if (SystemInfo.supportsMultisampledTextures == 0 || copyResolvedDepth)
cameraSamples = 1;
switch (cameraSamples)
{
case 8:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, true);
break;
case 4:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, true);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
case 2:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, true);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
// MSAA disabled, auto resolve supported or ms textures not supported
default:
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa2, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa4, false);
cmd.SetKeyword(ShaderGlobalKeywords.DepthMsaa8, false);
break;
}
cmd.SetKeyword(ShaderGlobalKeywords._OUTPUT_DEPTH, copyToDepth);
// We must perform a yflip if we're rendering into the backbuffer and we have a flipped source texture.
bool yflip = passData.cameraData.IsHandleYFlipped(source) && passData.isDstBackbuffer;
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Vector4 scaleBias = yflip ? new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y) : new Vector4(viewportScale.x, viewportScale.y, 0, 0);
// When we render to the backbuffer, we update the viewport to cover the entire screen just in case it hasn't been updated already.
if (passData.isDstBackbuffer)
cmd.SetViewport(passData.cameraData.pixelRect);
copyDepthMaterial.SetTexture(ShaderConstants._CameraDepthAttachment, source);
copyDepthMaterial.SetFloat(ShaderConstants._ZWriteShaderHandle, copyToDepth ? 1.0f : 0.0f);
Blitter.BlitTexture(cmd, source, scaleBias, copyDepthMaterial, 0);
}
}
///
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
// Disable obsolete warning for internal usage
#pragma warning disable CS0618
destination = k_CameraTarget;
#pragma warning restore CS0618
}
///
/// Sets up the Copy Depth pass for RenderGraph execution
///
/// The current RenderGraph used for recording and execution of a frame.
/// The renderer settings containing rendering data of the current frame.
/// TextureHandle of the destination it will copy to.
/// TextureHandle of the source it will copy from.
/// If this is true, the destination texture is bound as _CameraDepthTexture after the copy pass
/// The pass name used for debug and identifying the pass.
public void Render(RenderGraph renderGraph, ContextContainer frameData, TextureHandle destination, TextureHandle source, bool bindAsCameraDepth = false, string passName = "Copy Depth")
{
UniversalResourceData resourceData = frameData.Get();
UniversalCameraData cameraData = frameData.Get();
Render(renderGraph, destination, source, resourceData, cameraData, bindAsCameraDepth, passName);
}
///
/// Sets up the Copy Depth pass for RenderGraph execution
///
/// The current RenderGraph used for recording and execution of a frame.
/// TextureHandle of the destination it will copy to.
/// TextureHandle of the source it will copy from.
/// URP texture handles for the current frame.
/// Camera settings for the current frame.
/// If this is true, the destination texture is bound as _CameraDepthTexture after the copy pass
/// The pass name used for debug and identifying the pass.
public void Render(RenderGraph renderGraph, TextureHandle destination, TextureHandle source, UniversalResourceData resourceData, UniversalCameraData cameraData, bool bindAsCameraDepth = false, string passName = "Copy Depth")
{
// TODO RENDERGRAPH: should call the equivalent of Setup() to initialise everything correctly
MssaSamples = -1;
//Having a different pass name than profilingSampler.name is bad practice but this method was public before we cleaned up this naming
using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData, profilingSampler))
{
passData.copyDepthMaterial = m_CopyDepthMaterial;
passData.msaaSamples = MssaSamples;
passData.cameraData = cameraData;
passData.copyResolvedDepth = m_CopyResolvedDepth;
passData.copyToDepth = CopyToDepth || CopyToDepthXR;
passData.isDstBackbuffer = CopyToBackbuffer || CopyToDepthXR;
if (CopyToDepth)
{
// Writes depth using custom depth output
builder.SetRenderAttachmentDepth(destination, AccessFlags.WriteAll);
#if UNITY_EDITOR
// binding a dummy color target as a workaround to an OSX issue in Editor scene view (UUM-47698).
// Also required for preview camera rendering for grid drawn with builtin RP (UUM-55171).
if (cameraData.isSceneViewCamera || cameraData.isPreviewCamera)
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
#endif
}
else if (CopyToDepthXR)
{
// Writes depth using custom depth output
builder.SetRenderAttachmentDepth(destination, AccessFlags.WriteAll);
#if ENABLE_VR && ENABLE_XR_MODULE
// binding a dummy color target as a workaround to NRP depth only rendering limitation:
// "Attempting to render to a depth only surface with no dummy color attachment"
if (cameraData.xr.enabled && cameraData.xr.copyDepth)
builder.SetRenderAttachment(resourceData.backBufferColor, 0);
#endif
}
else
{
// Writes depth as "grayscale color" output
builder.SetRenderAttachment(destination, 0, AccessFlags.WriteAll);
}
passData.source = source;
builder.UseTexture(source, AccessFlags.Read);
if (bindAsCameraDepth && destination.IsValid())
builder.SetGlobalTextureAfterPass(destination, ShaderConstants._CameraDepthTexture);
// TODO RENDERGRAPH: culling? force culling off for testing
builder.AllowPassCulling(false);
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
ExecutePass(context.cmd, data, data.source);
});
}
}
}
}