using System.Collections.Generic; using XCEngine; using XCEngine.Rendering; namespace XCEngine.Rendering.Universal { public abstract class ScriptableRenderer { private readonly List m_features = new List(); private readonly List m_activePassQueue = new List(); private readonly RendererBlocks m_rendererBlocks = new RendererBlocks(); private bool m_isBuildingPassQueue; private CameraFrameStage m_passQueueStage = CameraFrameStage.MainScene; private bool m_disposed; protected ScriptableRenderer() { } internal void ReleaseRuntimeResourcesInstance() { if (m_disposed) { return; } ReleaseRuntimeResources(); for (int i = 0; i < m_features.Count; ++i) { ScriptableRendererFeature feature = m_features[i]; if (feature != null) { feature.ReleaseRuntimeResourcesInstance(); } } m_features.Clear(); m_activePassQueue.Clear(); m_rendererBlocks.Clear(); m_disposed = true; } public void EnqueuePass( ScriptableRenderPass renderPass) { if (renderPass == null) { return; } if (m_isBuildingPassQueue && !renderPass.SupportsStage(m_passQueueStage)) { return; } InsertActivePass( renderPass); } private void InsertActivePass( ScriptableRenderPass renderPass) { int insertIndex = m_activePassQueue.Count; while (insertIndex > 0 && m_activePassQueue[insertIndex - 1].renderPassEvent > renderPass.renderPassEvent) { insertIndex--; } m_activePassQueue.Insert( insertIndex, renderPass); } protected void AddFeature( ScriptableRendererFeature feature) { if (feature == null) { return; } m_features.Add(feature); feature.CreateInstance(); } protected virtual void AddRenderPasses( RenderingData renderingData) { } internal void AddFeatureInstance( ScriptableRendererFeature feature) { AddFeature(feature); } internal bool SupportsRendererRecordingInstance( RendererRecordingContext context) { return SupportsRendererRecording(context); } internal void ConfigureCameraFramePlanInstance( ScriptableRenderPipelinePlanningContext context) { ConfigureCameraFramePlan(context); } internal void FinalizeCameraFramePlanInstance( ScriptableRenderPipelinePlanningContext context) { FinalizeCameraFramePlan(context); } internal void ConfigureRenderSceneSetupInstance( RenderSceneSetupContext context) { ConfigureRenderSceneSetup(context); } internal void ConfigureDirectionalShadowExecutionStateInstance( DirectionalShadowExecutionContext context) { ConfigureDirectionalShadowExecutionState(context); } internal bool RecordRendererInstance( RendererRecordingContext context) { return RecordRenderer(context); } protected virtual bool SupportsRendererRecording( RendererRecordingContext context) { if (context == null) { return false; } RenderingData renderingData = context.renderingData; BuildPassQueue(renderingData); return SupportsRendererStage(context); } protected virtual bool RecordRenderer( RendererRecordingContext context) { if (context == null || context.renderContext == null) { return false; } RenderingData renderingData = context.renderingData; BuildPassQueue(renderingData); return RecordRendererStage(context); } protected internal virtual bool SupportsStageRenderGraph( CameraFrameStage stage) { return SupportsRendererRecording( new RendererRecordingContext(stage)); } protected internal virtual bool RecordStageRenderGraph( ScriptableRenderContext context) { return context != null && RecordRenderer( new RendererRecordingContext(context)); } protected virtual void ConfigureCameraFramePlan( ScriptableRenderPipelinePlanningContext context) { } protected virtual void FinalizeCameraFramePlan( ScriptableRenderPipelinePlanningContext context) { } protected virtual void ConfigureRenderSceneSetup( RenderSceneSetupContext context) { } protected virtual void ConfigureDirectionalShadowExecutionState( DirectionalShadowExecutionContext context) { } private bool HasRendererBlock( RendererBlock block) { return m_rendererBlocks.HasPasses(block); } protected virtual bool SupportsRendererStage( RendererRecordingContext context) { if (context == null) { return false; } switch (context.stage) { case CameraFrameStage.ShadowCaster: return HasRendererBlock( RendererBlock.ShadowCaster); case CameraFrameStage.DepthOnly: return HasRendererBlock( RendererBlock.DepthPrepass); case CameraFrameStage.MainScene: return HasRendererBlock( RendererBlock.MainOpaque) || HasRendererBlock( RendererBlock.MainSkybox) || HasRendererBlock( RendererBlock.MainTransparent); case CameraFrameStage.PostProcess: return HasRendererBlock( RendererBlock.PostProcess); case CameraFrameStage.FinalOutput: return HasRendererBlock( RendererBlock.FinalOutput); default: return false; } } protected virtual bool RecordRendererStage( RendererRecordingContext context) { if (context == null || context.renderContext == null) { return false; } bool recordedAnyPass = false; switch (context.stage) { case CameraFrameStage.ShadowCaster: return RecordRendererBlock( RendererBlock.ShadowCaster, context, ref recordedAnyPass) && recordedAnyPass; case CameraFrameStage.DepthOnly: return RecordRendererBlock( RendererBlock.DepthPrepass, context, ref recordedAnyPass) && recordedAnyPass; case CameraFrameStage.MainScene: return RecordRendererBlock( RendererBlock.MainOpaque, context, ref recordedAnyPass) && RecordRendererBlock( RendererBlock.MainSkybox, context, ref recordedAnyPass) && RecordRendererBlock( RendererBlock.MainTransparent, context, ref recordedAnyPass) && recordedAnyPass; case CameraFrameStage.PostProcess: return RecordRendererBlock( RendererBlock.PostProcess, context, ref recordedAnyPass) && recordedAnyPass; case CameraFrameStage.FinalOutput: return RecordRendererBlock( RendererBlock.FinalOutput, context, ref recordedAnyPass) && recordedAnyPass; default: return false; } } private bool RecordRendererBlock( RendererBlock block, RendererRecordingContext context, ref bool recordedAnyPass) { if (context == null || context.renderContext == null) { return false; } int firstPassIndex; int lastPassIndex; if (!m_rendererBlocks.TryGetPassRange( block, out firstPassIndex, out lastPassIndex)) { return true; } for (int i = firstPassIndex; i <= lastPassIndex; ++i) { ScriptableRenderPass renderPass = m_activePassQueue[i]; if (renderPass == null || !renderPass.SupportsRendererBlock(block)) { continue; } if (!renderPass.Record( context.renderContext, context.renderingData)) { return false; } recordedAnyPass = true; } return true; } private void BuildPassQueue( RenderingData renderingData) { m_activePassQueue.Clear(); m_rendererBlocks.Clear(); m_isBuildingPassQueue = true; m_passQueueStage = renderingData != null ? renderingData.stage : CameraFrameStage.MainScene; try { for (int i = 0; i < m_features.Count; ++i) { ScriptableRendererFeature feature = m_features[i]; if (feature == null || !feature.isActive) { continue; } feature.AddRenderPasses( this, renderingData); } AddRenderPasses(renderingData); m_rendererBlocks.Build(m_activePassQueue); } finally { m_isBuildingPassQueue = false; m_passQueueStage = CameraFrameStage.MainScene; } } protected virtual void ReleaseRuntimeResources() { } } }