using System; using XCEngine; using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { public abstract class ScriptableRenderPass { private const string kColorScaleFullscreenPassName = "Universal.ColorScaleFullscreen"; private const string kFinalColorFullscreenPassName = "Universal.FinalColorFullscreen"; private const string kShaderVectorFullscreenPassName = "Universal.ShaderVectorFullscreen"; private readonly CommandBuffer m_lifecycleCommandBuffer = new CommandBuffer("ScriptableRenderPass"); private ScriptableRenderPassInput m_input = ScriptableRenderPassInput.None; protected ScriptableRenderPass() { } public RenderPassEvent renderPassEvent { get; set; } = RenderPassEvent.BeforeRenderingOpaques; public ScriptableRenderPassInput input { get { return m_input; } } public bool requiresIntermediateTexture { get { return m_input != ScriptableRenderPassInput.None; } } protected ProfilingSampler profilingSampler { get; set; } = new ProfilingSampler("Unnamed_ScriptableRenderPass"); public void ConfigureInput( ScriptableRenderPassInput passInput) { m_input = passInput; } public virtual bool SupportsStage( CameraFrameStage stage) { RendererBlock block; return TryResolveRendererBlock( renderPassEvent, out block) && RendererBlockUtility.GetStage(block) == stage; } internal bool SupportsRendererBlock( RendererBlock block) { RendererBlock resolvedBlock; return TryResolveRendererBlock( renderPassEvent, out resolvedBlock) && resolvedBlock == block; } internal bool Record( ScriptableRenderContext context, RenderingData renderingData) { RenderingData passRenderingData = renderingData; OnCameraSetup( m_lifecycleCommandBuffer, ref passRenderingData); try { return RecordRenderGraph( context, passRenderingData); } finally { OnCameraCleanup( m_lifecycleCommandBuffer); } } public virtual void Execute( ScriptableRenderContext context, ref RenderingData renderingData) { } public virtual void OnCameraSetup( CommandBuffer cmd, ref RenderingData renderingData) { } public virtual void OnCameraCleanup( CommandBuffer cmd) { } public virtual void OnFinishCameraStackRendering( CommandBuffer cmd) { } protected virtual bool RecordRenderGraph( ScriptableRenderContext context, RenderingData renderingData) { if (context == null || renderingData == null) { return false; } Execute( context, ref renderingData); return true; } public static bool operator >( ScriptableRenderPass lhs, ScriptableRenderPass rhs) { return CompareRenderPassEvents( lhs, rhs) > 0; } public static bool operator <( ScriptableRenderPass lhs, ScriptableRenderPass rhs) { return CompareRenderPassEvents( lhs, rhs) < 0; } protected bool RecordColorScaleFullscreenPass( ScriptableRenderContext context, Vector4 colorScale) { RenderGraphTextureHandle sourceColor; RenderGraphTextureHandle outputColor; return TryResolveDefaultFullscreenTargets( context, out sourceColor, out outputColor) && RecordColorScaleFullscreenPass( context, sourceColor, outputColor, colorScale); } protected bool RecordColorScaleFullscreenPass( ScriptableRenderContext context, RenderGraphTextureHandle sourceColor, RenderGraphTextureHandle outputColor, Vector4 colorScale, string passName = null) { if (context == null || !outputColor.isValid) { return false; } RenderGraphRasterPassBuilder passBuilder = context.AddRasterPass( ResolveFullscreenPassName( passName, kColorScaleFullscreenPassName)); if (sourceColor.isValid) { passBuilder.UseColorSource(sourceColor); } return passBuilder .SetColorAttachment(outputColor) .SetColorScaleFullscreenExecution(colorScale) .Commit(); } protected bool RecordShaderVectorFullscreenPass( ScriptableRenderContext context, string shaderPath, Vector4 vectorPayload, string passName = null) { if (string.IsNullOrEmpty(shaderPath)) { throw new ArgumentException( "Fullscreen shader path cannot be null or empty.", nameof(shaderPath)); } RenderGraphTextureHandle sourceColor; RenderGraphTextureHandle outputColor; return TryResolveDefaultFullscreenTargets( context, out sourceColor, out outputColor) && RecordShaderVectorFullscreenPass( context, sourceColor, outputColor, shaderPath, vectorPayload, passName: passName); } protected bool RecordShaderVectorFullscreenPass( ScriptableRenderContext context, RenderGraphTextureHandle sourceColor, RenderGraphTextureHandle outputColor, string shaderPath, Vector4 vectorPayload, string shaderPassName = null, string passName = null) { if (string.IsNullOrEmpty(shaderPath)) { throw new ArgumentException( "Fullscreen shader path cannot be null or empty.", nameof(shaderPath)); } if (context == null || !outputColor.isValid) { return false; } RenderGraphRasterPassBuilder passBuilder = context.AddRasterPass( ResolveFullscreenPassName( passName, kShaderVectorFullscreenPassName)); if (sourceColor.isValid) { passBuilder.UseColorSource(sourceColor); } return passBuilder .SetColorAttachment(outputColor) .SetShaderVectorFullscreenExecution( shaderPath, vectorPayload, shaderPassName) .Commit(); } protected bool RecordFinalColorFullscreenPass( ScriptableRenderContext context, FinalColorSettings settings, string passName = null) { RenderGraphTextureHandle sourceColor; RenderGraphTextureHandle outputColor; return TryResolveDefaultFullscreenTargets( context, out sourceColor, out outputColor) && RecordFinalColorFullscreenPass( context, sourceColor, outputColor, settings, passName); } protected bool RecordFinalColorFullscreenPass( ScriptableRenderContext context, RenderGraphTextureHandle sourceColor, RenderGraphTextureHandle outputColor, FinalColorSettings settings, string passName = null) { if (context == null || !outputColor.isValid) { return false; } RenderGraphRasterPassBuilder passBuilder = context.AddRasterPass( ResolveFullscreenPassName( passName, kFinalColorFullscreenPassName)); if (sourceColor.isValid) { passBuilder.UseColorSource(sourceColor); } return passBuilder .SetColorAttachment(outputColor) .SetFinalColorFullscreenExecution(settings) .Commit(); } internal static bool TryResolveStage( RenderPassEvent passEvent, out CameraFrameStage stage) { RendererBlock block; if (TryResolveRendererBlock( passEvent, out block)) { stage = RendererBlockUtility.GetStage(block); return true; } stage = CameraFrameStage.MainScene; return false; } internal static bool TryResolveRendererBlock( RenderPassEvent passEvent, out RendererBlock block) { switch (passEvent) { case RenderPassEvent.BeforeRenderingShadows: case RenderPassEvent.AfterRenderingShadows: block = RendererBlock.ShadowCaster; return true; case RenderPassEvent.BeforeRenderingPrePasses: case RenderPassEvent.AfterRenderingPrePasses: block = RendererBlock.DepthPrepass; return true; case RenderPassEvent.BeforeRenderingGbuffer: case RenderPassEvent.AfterRenderingGbuffer: case RenderPassEvent.BeforeRenderingDeferredLights: case RenderPassEvent.AfterRenderingDeferredLights: case RenderPassEvent.BeforeRenderingOpaques: case RenderPassEvent.RenderOpaques: case RenderPassEvent.AfterRenderingOpaques: block = RendererBlock.MainOpaque; return true; case RenderPassEvent.BeforeRenderingSkybox: case RenderPassEvent.RenderSkybox: case RenderPassEvent.AfterRenderingSkybox: block = RendererBlock.MainSkybox; return true; case RenderPassEvent.BeforeRenderingTransparents: case RenderPassEvent.RenderTransparents: case RenderPassEvent.AfterRenderingTransparents: block = RendererBlock.MainTransparent; return true; case RenderPassEvent.BeforeRenderingPostProcessing: case RenderPassEvent.AfterRenderingPostProcessing: block = RendererBlock.PostProcess; return true; case RenderPassEvent.BeforeRenderingFinalOutput: case RenderPassEvent.AfterRendering: case RenderPassEvent.AfterRenderingFinalOutput: block = RendererBlock.FinalOutput; return true; default: block = RendererBlock.MainOpaque; return false; } } private static int CompareRenderPassEvents( ScriptableRenderPass lhs, ScriptableRenderPass rhs) { if (ReferenceEquals( lhs, rhs)) { return 0; } if (lhs == null) { return -1; } if (rhs == null) { return 1; } return lhs.renderPassEvent.CompareTo( rhs.renderPassEvent); } private static bool TryResolveDefaultFullscreenTargets( ScriptableRenderContext context, out RenderGraphTextureHandle sourceColor, out RenderGraphTextureHandle outputColor) { sourceColor = new RenderGraphTextureHandle(); outputColor = new RenderGraphTextureHandle(); if (context == null) { return false; } sourceColor = context.sourceColorTexture; outputColor = context.primaryColorTarget; return sourceColor.isValid && outputColor.isValid; } private static string ResolveFullscreenPassName( string passName, string defaultPassName) { return string.IsNullOrEmpty(passName) ? defaultPassName : passName; } } }