using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.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; }
Material m_CopyDepthMaterial;
internal bool m_CopyResolvedDepth;
internal bool m_ShouldClear;
private PassData m_PassData;
///
/// Creates a new CopyDepthPass instance.
///
/// The RenderPassEvent to use.
/// The Material 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.
///
public CopyDepthPass(RenderPassEvent evt, Material copyDepthMaterial, bool shouldClear = false, bool copyToDepth = false, bool copyResolvedDepth = false)
{
base.profilingSampler = new ProfilingSampler(nameof(CopyDepthPass));
m_PassData = new PassData();
CopyToDepth = copyToDepth;
m_CopyDepthMaterial = copyDepthMaterial;
renderPassEvent = evt;
m_CopyResolvedDepth = copyResolvedDepth;
m_ShouldClear = shouldClear;
}
///
/// 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;
}
///
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
var descriptor = renderingData.cameraData.cameraTargetDescriptor;
var isDepth = (destination.rt && destination.rt.graphicsFormat == GraphicsFormat.None);
descriptor.graphicsFormat = isDepth ? GraphicsFormat.D32_SFloat_S8_UInt : GraphicsFormat.R32_SFloat;
descriptor.msaaSamples = 1;
// 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 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);
}
private class PassData
{
internal TextureHandle source;
internal TextureHandle destination;
internal CommandBuffer cmd;
internal CameraData cameraData;
internal Material copyDepthMaterial;
internal int msaaSamples;
internal bool copyResolvedDepth;
internal bool copyToDepth;
}
///
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
m_PassData.copyDepthMaterial = m_CopyDepthMaterial;
m_PassData.msaaSamples = MssaSamples;
m_PassData.copyResolvedDepth = m_CopyResolvedDepth;
m_PassData.copyToDepth = CopyToDepth;
renderingData.commandBuffer.SetGlobalTexture("_CameraDepthAttachment", source.nameID);
ExecutePass(context, m_PassData, ref renderingData.commandBuffer, ref renderingData.cameraData, source, destination);
}
private static void ExecutePass(ScriptableRenderContext context, PassData passData, ref CommandBuffer cmd, ref CameraData cameraData, RTHandle source, RTHandle destination)
{
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)
{
RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor;
cameraSamples = descriptor.msaaSamples;
}
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.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
break;
case 4:
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
break;
case 2:
cmd.EnableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
break;
// MSAA disabled, auto resolve supported or ms textures not supported
default:
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa2);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa4);
cmd.DisableShaderKeyword(ShaderKeywordStrings.DepthMsaa8);
break;
}
if (copyToDepth || destination.rt.graphicsFormat == GraphicsFormat.None)
cmd.EnableShaderKeyword("_OUTPUT_DEPTH");
else
cmd.DisableShaderKeyword("_OUTPUT_DEPTH");
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
// We y-flip if
// 1) we are blitting from render texture to back buffer(UV starts at bottom) and
// 2) renderTexture starts UV at top
bool isGameViewFinalTarget = cameraData.cameraType == CameraType.Game && destination.nameID == BuiltinRenderTextureType.CameraTarget;
#if ENABLE_VR && ENABLE_XR_MODULE
if (cameraData.xr.enabled)
{
if (cameraData.xr.supportsFoveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
isGameViewFinalTarget |= new RenderTargetIdentifier(destination.nameID, 0, CubemapFace.Unknown, 0) == new RenderTargetIdentifier(cameraData.xr.renderTarget, 0, CubemapFace.Unknown, 0);
}
#endif
bool yflip = cameraData.IsHandleYFlipped(source) != cameraData.IsHandleYFlipped(destination);
Vector4 scaleBias = yflip ? new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y) : new Vector4(viewportScale.x, viewportScale.y, 0, 0);
if (isGameViewFinalTarget)
cmd.SetViewport(cameraData.pixelRect);
else
cmd.SetViewport(new Rect(0, 0, cameraData.cameraTargetDescriptor.width, cameraData.cameraTargetDescriptor.height));
Blitter.BlitTexture(cmd, source, scaleBias, copyDepthMaterial, 0);
}
}
///
public override void OnCameraCleanup(CommandBuffer cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
destination = k_CameraTarget;
}
internal void Render(RenderGraph renderGraph, out TextureHandle destination, in TextureHandle source, ref RenderingData renderingData)
{
// TODO RENDERGRAPH: should call the equivalent of Setup() to initialise everything correctly
MssaSamples = -1;
// TODO RENDERGRAPH: should refactor this as utility method for other passes to set Global textures
using (var builder = renderGraph.AddRenderPass("Setup Global Depth", out var passData, base.profilingSampler))
{
passData.source = builder.ReadTexture(source);
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RenderGraphContext context) =>
{
context.cmd.SetGlobalTexture("_CameraDepthAttachment", data.source);
});
}
using (var builder = renderGraph.AddRenderPass("Copy Depth", out var passData, base.profilingSampler))
{
var depthDescriptor = renderingData.cameraData.cameraTargetDescriptor;
depthDescriptor.graphicsFormat = GraphicsFormat.R32_SFloat;
depthDescriptor.depthStencilFormat = GraphicsFormat.None;
depthDescriptor.depthBufferBits = 0;
depthDescriptor.msaaSamples = 1;// Depth-Only pass don't use MSAA
destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDescriptor, "_CameraDepthTexture", true);
passData.copyDepthMaterial = m_CopyDepthMaterial;
passData.msaaSamples = MssaSamples;
passData.cameraData = renderingData.cameraData;
passData.cmd = renderingData.commandBuffer;
passData.copyResolvedDepth = m_CopyResolvedDepth;
passData.copyToDepth = CopyToDepth;
passData.source = builder.ReadTexture(source);
passData.destination = builder.UseColorBuffer(destination, 0);
// TODO RENDERGRAPH: culling? force culling off for testing
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RenderGraphContext context) =>
{
ExecutePass(context.renderContext, data, ref data.cmd, ref data.cameraData, data.source, data.destination);
});
}
using (var builder = renderGraph.AddRenderPass("Setup Global Copy Depth", out var passData, base.profilingSampler))
{
passData.cmd = renderingData.commandBuffer;
passData.destination = builder.UseColorBuffer(destination, 0);
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, RenderGraphContext context) =>
{
data.cmd.SetGlobalTexture("_CameraDepthTexture", data.destination);
});
}
}
}
}