using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.Universal
{
sealed class MotionVectorRenderPass : ScriptableRenderPass
{
internal const string k_MotionVectorTextureName = "_MotionVectorTexture";
internal const string k_MotionVectorDepthTextureName = "_MotionVectorDepthTexture";
internal const GraphicsFormat k_TargetFormat = GraphicsFormat.R16G16_SFloat;
public const string k_MotionVectorsLightModeTag = "MotionVectors";
static readonly string[] s_ShaderTags = new string[] { k_MotionVectorsLightModeTag };
static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture");
static readonly ProfilingSampler s_SetMotionMatrixProfilingSampler = new ProfilingSampler("Set Motion Vector Global Matrices");
readonly Material m_CameraMaterial;
readonly FilteringSettings m_FilteringSettings;
internal MotionVectorRenderPass(RenderPassEvent evt, Material cameraMaterial, LayerMask opaqueLayerMask)
{
profilingSampler = ProfilingSampler.Get(URPProfileId.DrawMotionVectors);
renderPassEvent = evt;
m_CameraMaterial = cameraMaterial;
m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque,opaqueLayerMask);
ConfigureInput(ScriptableRenderPassInput.Depth);
}
private static void ExecutePass(RasterCommandBuffer cmd, PassData passData, RendererList rendererList)
{
var cameraMaterial = passData.cameraMaterial;
if (cameraMaterial == null)
return;
// Get data
Camera camera = passData.camera;
// Never draw in Preview
if (camera.cameraType == CameraType.Preview)
return;
// These flags are still required in SRP or the engine won't compute previous model matrices...
// If the flag hasn't been set yet on this camera, motion vectors will skip a frame.
camera.depthTextureMode |= DepthTextureMode.MotionVectors | DepthTextureMode.Depth;
// TODO: add option to only draw either one?
DrawCameraMotionVectors(cmd, passData.xr, cameraMaterial);
DrawObjectMotionVectors(cmd, passData.xr, ref rendererList);
}
private static DrawingSettings GetDrawingSettings(Camera camera, bool supportsDynamicBatching)
{
var sortingSettings = new SortingSettings(camera) { criteria = SortingCriteria.CommonOpaque };
var drawingSettings = new DrawingSettings(ShaderTagId.none, sortingSettings)
{
perObjectData = PerObjectData.MotionVectors,
enableDynamicBatching = supportsDynamicBatching,
enableInstancing = true,
lodCrossFadeStencilMask = 0, // Disable stencil-based lod because depth copy before motion vector pass doesn't copy stencils.
};
for (int i = 0; i < s_ShaderTags.Length; ++i)
{
drawingSettings.SetShaderPassName(i, new ShaderTagId(s_ShaderTags[i]));
}
return drawingSettings;
}
// NOTE: depends on camera depth to reconstruct static geometry positions
private static void DrawCameraMotionVectors(RasterCommandBuffer cmd, XRPass xr, Material cameraMaterial)
{
#if ENABLE_VR && ENABLE_XR_MODULE
bool foveatedRendering = xr.supportsFoveatedRendering;
bool nonUniformFoveatedRendering = foveatedRendering && XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster);
if (foveatedRendering)
{
if (nonUniformFoveatedRendering)
// This is a screen-space pass, make sure foveated rendering is disabled for non-uniform renders
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
else
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled);
}
#endif
// Draw fullscreen quad
cmd.DrawProcedural(Matrix4x4.identity, cameraMaterial, 0, MeshTopology.Triangles, 3, 1);
#if ENABLE_VR && ENABLE_XR_MODULE
if (foveatedRendering && !nonUniformFoveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
#endif
}
private static void DrawObjectMotionVectors(RasterCommandBuffer cmd, XRPass xr, ref RendererList rendererList)
{
#if ENABLE_VR && ENABLE_XR_MODULE
bool foveatedRendering = xr.supportsFoveatedRendering;
if (foveatedRendering)
// This is a geometry pass, enable foveated rendering (we need to disable it after)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Enabled);
#endif
cmd.DrawRendererList(rendererList);
#if ENABLE_VR && ENABLE_XR_MODULE
if (foveatedRendering)
cmd.SetFoveatedRenderingMode(FoveatedRenderingMode.Disabled);
#endif
}
/// <summary>
/// Shared pass data
/// </summary>
private class PassData
{
internal Camera camera;
internal XRPass xr;
internal TextureHandle cameraDepth;
internal Material cameraMaterial;
internal RendererListHandle rendererListHdl;
// Required for code sharing purpose between RG and non-RG.
internal RendererList rendererList;
}
/// <summary>
/// Initialize the shared pass data.
/// </summary>
/// <param name="passData"></param>
private void InitPassData(ref PassData passData, UniversalCameraData cameraData)
{
passData.camera = cameraData.camera;
passData.xr = cameraData.xr;
passData.cameraMaterial = m_CameraMaterial;
}
private void InitRendererLists(ref PassData passData, ref CullingResults cullResults, bool supportsDynamicBatching, RenderGraph renderGraph)
{
var drawingSettings = GetDrawingSettings(passData.camera, supportsDynamicBatching);
var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, drawingSettings, m_FilteringSettings, renderStateBlock, ref passData.rendererListHdl);
}
internal void Render(RenderGraph renderGraph, ContextContainer frameData, TextureHandle cameraDepthTexture, TextureHandle motionVectorColor, TextureHandle motionVectorDepth)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
builder.UseAllGlobalTextures(true);
builder.AllowGlobalStateModification(true);
if (cameraData.xr.enabled)
{
builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses);
// Apply MultiviewRenderRegionsCompatible flag only to the peripheral view in Quad Views
if (cameraData.xr.multipassId == 0)
{
builder.SetExtendedFeatureFlags(ExtendedFeatureFlags.MultiviewRenderRegionsCompatible);
}
}
builder.SetRenderAttachment(motionVectorColor, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(motionVectorDepth, AccessFlags.ReadWrite);
InitPassData(ref passData, cameraData);
passData.cameraDepth = cameraDepthTexture;
builder.UseTexture(cameraDepthTexture, AccessFlags.Read);
InitRendererLists(ref passData, ref renderingData.cullResults, renderingData.supportsDynamicBatching, renderGraph);
builder.UseRendererList(passData.rendererListHdl);
if (motionVectorColor.IsValid())
builder.SetGlobalTextureAfterPass(motionVectorColor, Shader.PropertyToID(k_MotionVectorTextureName));
if (motionVectorDepth.IsValid())
builder.SetGlobalTextureAfterPass(motionVectorDepth, Shader.PropertyToID(k_MotionVectorDepthTextureName));
builder.SetRenderFunc(static (PassData data, RasterGraphContext context) =>
{
if (data.cameraMaterial != null)
data.cameraMaterial.SetTexture(s_CameraDepthTextureID, data.cameraDepth);
ExecutePass(context.cmd, data, data.rendererListHdl);
});
}
}
// Global motion vector matrix setup pass.
// Used for MotionVector passes and also read in VFX early compute shader
public class MotionMatrixPassData
{
public MotionVectorsPersistentData motionData;
public XRPass xr;
}
internal static void SetRenderGraphMotionVectorGlobalMatrices(RenderGraph renderGraph, UniversalCameraData cameraData)
{
if (cameraData.camera.TryGetComponent<UniversalAdditionalCameraData>(out var additionalCameraData))
{
using (var builder = renderGraph.AddRasterRenderPass<MotionMatrixPassData>(s_SetMotionMatrixProfilingSampler.name, out var passData, s_SetMotionMatrixProfilingSampler))
{
passData.motionData = additionalCameraData.motionVectorsPersistentData;
passData.xr = cameraData.xr;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc(static (MotionMatrixPassData data, RasterGraphContext context) =>
{
data.motionData.SetGlobalMotionMatrices(context.cmd, data.xr);
});
}
}
}
}
}