using System; using Unity.Collections; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; namespace UnityEngine.Rendering.Universal { sealed class MotionVectorRenderPass : ScriptableRenderPass { #region Fields 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"); RTHandle m_Color; RTHandle m_Depth; readonly Material m_CameraMaterial; readonly FilteringSettings m_FilteringSettings; private PassData m_PassData; #endregion #region Constructors 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); m_PassData = new PassData(); ConfigureInput(ScriptableRenderPassInput.Depth); } #endregion #region State internal void Setup(RTHandle color, RTHandle depth) { m_Color = color; m_Depth = depth; } [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { cmd.SetGlobalTexture(m_Color.name, m_Color.nameID); cmd.SetGlobalTexture(m_Depth.name, m_Depth.nameID); // Disable obsolete warning for internal usage #pragma warning disable CS0618 ConfigureTarget(m_Color, m_Depth); ConfigureClear(ClearFlag.Color | ClearFlag.Depth, Color.black); // Can become a Store based on 'StoreActionsOptimization.Auto' and/or if a user RendererFeature is added. // We need to keep the MotionVecDepth in case of a user wants to extend the motion vectors // using a custom RendererFeature. ConfigureDepthStoreAction(RenderBufferStoreAction.DontCare); #pragma warning restore CS0618 } #endregion #region Execution 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); } [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { ContextContainer frameData = renderingData.frameData; UniversalRenderingData universalRenderingData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); var cmd = CommandBufferHelpers.GetRasterCommandBuffer(renderingData.commandBuffer); // Profiling command using (new ProfilingScope(cmd,profilingSampler)) { InitPassData(ref m_PassData, cameraData); InitRendererLists(ref m_PassData, ref universalRenderingData.cullResults, universalRenderingData.supportsDynamicBatching, context, default(RenderGraph), false); ExecutePass(cmd, m_PassData, m_PassData.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, }; 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 } #endregion /// /// Shared pass data /// private class PassData { internal Camera camera; internal XRPass xr; internal TextureHandle motionVectorColor; internal TextureHandle motionVectorDepth; internal TextureHandle cameraDepth; internal Material cameraMaterial; internal RendererListHandle rendererListHdl; // Required for code sharing purpose between RG and non-RG. internal RendererList rendererList; } /// /// Initialize the shared pass data. /// /// 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, ScriptableRenderContext context, RenderGraph renderGraph, bool useRenderGraph) { var drawingSettings = GetDrawingSettings(passData.camera, supportsDynamicBatching); var renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); if (useRenderGraph) RenderingUtils.CreateRendererListWithRenderStateBlock(renderGraph, ref cullResults, drawingSettings, m_FilteringSettings, renderStateBlock, ref passData.rendererListHdl); else RenderingUtils.CreateRendererListWithRenderStateBlock(context, ref cullResults, drawingSettings, m_FilteringSettings, renderStateBlock, ref passData.rendererList); } internal void Render(RenderGraph renderGraph, ContextContainer frameData, TextureHandle cameraDepthTexture, TextureHandle motionVectorColor, TextureHandle motionVectorDepth) { UniversalRenderingData renderingData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); using (var builder = renderGraph.AddRasterRenderPass(passName, out var passData, profilingSampler)) { builder.UseAllGlobalTextures(true); // TODO RENDERGRAPH: culling? force culling off for testing builder.AllowPassCulling(false); builder.AllowGlobalStateModification(true); if (cameraData.xr.enabled) builder.EnableFoveatedRasterization(cameraData.xr.supportsFoveatedRendering && cameraData.xrUniversal.canFoveateIntermediatePasses); passData.motionVectorColor = motionVectorColor; builder.SetRenderAttachment(motionVectorColor, 0, AccessFlags.Write); passData.motionVectorDepth = motionVectorDepth; builder.SetRenderAttachmentDepth(motionVectorDepth, AccessFlags.Write); InitPassData(ref passData, cameraData); passData.cameraDepth = cameraDepthTexture; builder.UseTexture(cameraDepthTexture, AccessFlags.Read); InitRendererLists(ref passData, ref renderingData.cullResults, renderingData.supportsDynamicBatching, default(ScriptableRenderContext), renderGraph, true); 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((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 SetMotionVectorGlobalMatrices(CommandBuffer cmd, UniversalCameraData cameraData) { if (cameraData.camera.TryGetComponent(out var additionalCameraData)) { additionalCameraData.motionVectorsPersistentData?.SetGlobalMotionMatrices(CommandBufferHelpers.GetRasterCommandBuffer(cmd), cameraData.xr); } } internal static void SetRenderGraphMotionVectorGlobalMatrices(RenderGraph renderGraph, UniversalCameraData cameraData) { if (cameraData.camera.TryGetComponent(out var additionalCameraData)) { using (var builder = renderGraph.AddRasterRenderPass(s_SetMotionMatrixProfilingSampler.name, out var passData, s_SetMotionMatrixProfilingSampler)) { passData.motionData = additionalCameraData.motionVectorsPersistentData; passData.xr = cameraData.xr; builder.AllowPassCulling(false); builder.AllowGlobalStateModification(true); builder.SetRenderFunc(static (MotionMatrixPassData data, RasterGraphContext context) => { data.motionData.SetGlobalMotionMatrices(context.cmd, data.xr); }); } } } } }