using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal.Internal; /// /// The class for the On-Tile Post Processing renderer feature. This renderer feature provides a reduced scope alternative to the built-in URP post-processing features but that can run more optimally on tile-based graphics hardware (most untethered-XR devices) /// The renderer feature could only be added once. Adding multiple post processing passes is currently not supported. /// [DisallowMultipleRendererFeature("On Tile Post Processing (Untethered XR)")] public partial class OnTilePostProcessFeature : ScriptableRendererFeature { [SerializeField, HideInInspector] PostProcessData m_PostProcessData; Shader m_UberPostShader; /// /// Specifies at which injection point the pass will be rendered. /// RenderPassEvent postProcessingEvent = RenderPassEvent.AfterRenderingPostProcessing-1; Material m_OnTilePostProcessMaterial; ColorGradingLutPass m_ColorGradingLutPass; OnTilePostProcessPass m_OnTilePostProcessPass; bool TryLoadResources() { if (m_UberPostShader == null || m_OnTilePostProcessMaterial == null) { if (!GraphicsSettings.TryGetRenderPipelineSettings(out var resources)) { Debug.LogErrorFormat( $"Couldn't find the required resources for the {nameof(OnTilePostProcessFeature)} render feature."); return false; } m_UberPostShader = resources.uberPostShader; m_OnTilePostProcessMaterial = new Material(m_UberPostShader); } return true; } /// public override void Create() { if (m_PostProcessData == null) { #if UNITY_EDITOR m_PostProcessData = PostProcessData.GetDefaultPostProcessData(); #endif } if (m_PostProcessData != null) { m_ColorGradingLutPass = new ColorGradingLutPass(RenderPassEvent.BeforeRenderingPrePasses, m_PostProcessData); m_OnTilePostProcessPass = new OnTilePostProcessPass(m_PostProcessData); // On-tile PP requries memoryless intermediate texture to work. In case intermediate texture is not memoryless, on-tile PP will falls back to offtile rendering. m_OnTilePostProcessPass.requiresIntermediateTexture = true; } } #if ENABLE_VR && ENABLE_XR_MODULE bool IsRuntimePlatformUntetheredXR() { // Return true if the current runtime platform is Android(untethered XR platform) return Application.platform == RuntimePlatform.Android; } #if UNITY_EDITOR bool IsBuildTargetUntetheredXR() { // Return true if the current build target is Android(untethered XR platform). return EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android; } #endif #endif /// public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { bool useFallback = true; #if ENABLE_VR && ENABLE_XR_MODULE // Enable on-tile post processing when running untethered XR if (renderingData.cameraData.xr.enabled && IsRuntimePlatformUntetheredXR()) { useFallback = false; } #if UNITY_EDITOR // Enable on-tile post processing when running XR Editor Playmode (build target needs to be Android) if (renderingData.cameraData.xr.enabled && IsBuildTargetUntetheredXR()) { useFallback = false; } #endif #endif // Post processing needs to be enabled on the camera if (!renderingData.cameraData.postProcessEnabled) return; // NOTE: Ideally, we check here if the Post Processing is enabled on the UniversalRenderer asset through a public API. In that case, the built in post processing will be enabled. // We currently do not have a public API for that, so we use internal API for now var universalRenderer = renderer as UniversalRenderer; if (universalRenderer.isPostProcessPassRenderGraphActive) { Debug.LogError("URP renderer(Universal Renderer Data) has post processing enabled, which conflicts with the On-Tile post processing feature. Only one of the post processing should be enabled. On-Tile post processing feature will not be added."); return; } if (m_ColorGradingLutPass == null || m_OnTilePostProcessPass == null) return; if (!TryLoadResources()) return; var graphicsDeviceType = SystemInfo.graphicsDeviceType; var deviceSupportsFbf = graphicsDeviceType == GraphicsDeviceType.Vulkan || graphicsDeviceType == GraphicsDeviceType.Metal || graphicsDeviceType == GraphicsDeviceType.Direct3D12; if (!deviceSupportsFbf) { Debug.LogError("The On-Tile post processing feature is not supported on the graphics devices that don't support frame buffer fetch."); return; } // Internally force the correct mode we require while this is not a public setting. UniversalRenderPipeline.renderTextureUVOriginStrategy = RenderTextureUVOriginStrategy.PropagateAttachmentOrientation; m_ColorGradingLutPass.renderPassEvent = RenderPassEvent.BeforeRenderingPrePasses; m_OnTilePostProcessPass.Setup(ref m_OnTilePostProcessMaterial); m_OnTilePostProcessPass.renderPassEvent = postProcessingEvent; if (useFallback) { // Perform fallback logic to 1. use texture read(off-tile rendering) and 2. disable the UV origin propagation mode. m_OnTilePostProcessPass.m_UseTextureReadFallback = true; UniversalRenderPipeline.renderTextureUVOriginStrategy = RenderTextureUVOriginStrategy.BottomLeft; } else { m_OnTilePostProcessPass.m_UseTextureReadFallback = false; } renderer.EnqueuePass(m_ColorGradingLutPass); renderer.EnqueuePass(m_OnTilePostProcessPass); } /// protected override void Dispose(bool disposing) { // always dispose unmanaged resources m_ColorGradingLutPass?.Cleanup(); } }