Files
XCEngine/managed/XCEngine.RenderPipelines.Universal/Rendering/Universal/ScriptableRenderer.cs

918 lines
28 KiB
C#

using System.Collections.Generic;
using XCEngine;
using XCEngine.Rendering;
using XCEngine.Rendering.RenderGraphModule;
namespace XCEngine.Rendering.Universal
{
public abstract class ScriptableRenderer
{
private const int kMaxFramePlanSnapshotCount = 64;
private struct PassQueueStageManifest
{
public bool shadowCaster;
public bool depthOnly;
public bool postProcess;
public bool finalOutput;
}
private sealed class RendererFramePlan
{
public RendererFramePlan(
ulong framePlanId,
int rendererIndex,
List<ScriptableRenderPass> activePassQueue)
{
this.framePlanId = framePlanId;
this.rendererIndex = rendererIndex;
this.activePassQueue =
activePassQueue ??
new List<ScriptableRenderPass>();
}
public readonly ulong framePlanId;
public readonly int rendererIndex;
public readonly List<ScriptableRenderPass> activePassQueue;
public PassQueueStageManifest manifest;
}
private readonly List<ScriptableRendererFeature> m_features =
new List<ScriptableRendererFeature>();
private readonly List<ScriptableRenderPass> m_activePassQueue =
new List<ScriptableRenderPass>();
private readonly RendererBlocks m_rendererBlocks =
new RendererBlocks();
private readonly Dictionary<ulong, RendererFramePlan>
m_framePlans =
new Dictionary<ulong, RendererFramePlan>();
private readonly CommandBuffer m_finishCameraStackCommandBuffer =
new CommandBuffer("ScriptableRenderer.FinishCameraStack");
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_framePlans.Clear();
m_disposed = true;
}
public void EnqueuePass(
ScriptableRenderPass renderPass)
{
if (renderPass == null)
{
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);
ConfigureRendererFeaturesCameraFramePlan(context);
}
internal void ConfigurePassQueueCameraFramePlanInstance(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null)
{
return;
}
RendererFramePlan framePlan =
BuildRendererFramePlan(context);
ApplyPassQueueStageManifest(context, framePlan.manifest);
StoreRendererFramePlan(framePlan);
ClearPassQueue();
}
internal void FinalizeCameraFramePlanInstance(
ScriptableRenderPipelinePlanningContext context)
{
FinalizeCameraFramePlan(context);
}
internal void ConfigureRenderSceneSetupInstance(
RenderSceneSetupContext context)
{
ConfigureRenderSceneSetup(context);
ConfigureRendererFeaturesRenderSceneSetup(context);
}
internal void ConfigureDirectionalShadowExecutionStateInstance(
DirectionalShadowExecutionContext context)
{
ConfigureDirectionalShadowExecutionState(context);
ConfigureRendererFeaturesDirectionalShadowExecutionState(
context);
}
internal bool RecordRendererInstance(
RendererRecordingContext context)
{
return RecordRenderer(context);
}
protected virtual bool SupportsRendererRecording(
RendererRecordingContext context)
{
if (context == null)
{
return false;
}
RenderingData renderingData =
context.renderingData;
RendererFramePlan framePlan;
if (TryGetRendererFramePlan(
context,
out framePlan))
{
return SupportsRendererRecordingFromFramePlan(
context,
framePlan);
}
if (context.framePlanId != 0UL)
{
return false;
}
BuildPassQueue(renderingData);
try
{
return SupportsRendererStage(context);
}
finally
{
ClearPassQueue();
}
}
protected virtual bool RecordRenderer(
RendererRecordingContext context)
{
if (context == null ||
context.renderContext == null)
{
return false;
}
RenderingData renderingData =
context.renderingData;
RendererFramePlan framePlan;
if (TryGetRendererFramePlan(
context,
out framePlan))
{
return RecordRendererFromFramePlan(
context,
framePlan);
}
if (context.framePlanId != 0UL)
{
return false;
}
BuildPassQueue(renderingData);
try
{
bool recorded;
using (ContextContainer frameData =
ScriptableRenderPass.BuildRenderGraphFrameData(
renderingData))
{
recorded =
RecordRendererStage(
context,
new RenderGraph(context.renderContext),
frameData);
}
if (recorded)
{
FinishCameraStackRendering();
}
return recorded;
}
finally
{
ClearPassQueue();
}
}
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 void ConfigureRendererFeaturesCameraFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
for (int i = 0; i < m_features.Count; ++i)
{
ScriptableRendererFeature feature = m_features[i];
if (feature != null &&
feature.isActive)
{
feature.ConfigureCameraFramePlan(context);
}
}
}
private void ConfigureRendererFeaturesRenderSceneSetup(
RenderSceneSetupContext context)
{
for (int i = 0; i < m_features.Count; ++i)
{
ScriptableRendererFeature feature = m_features[i];
if (feature != null &&
feature.isActive)
{
feature.ConfigureRenderSceneSetup(context);
}
}
}
private void ConfigureRendererFeaturesDirectionalShadowExecutionState(
DirectionalShadowExecutionContext context)
{
for (int i = 0; i < m_features.Count; ++i)
{
ScriptableRendererFeature feature = m_features[i];
if (feature != null &&
feature.isActive)
{
feature.ConfigureDirectionalShadowExecutionState(
context);
}
}
}
private bool HasRendererBlock(
RendererBlock block)
{
return m_rendererBlocks.HasPasses(block);
}
private static bool HasRendererBlock(
RendererBlocks rendererBlocks,
RendererBlock block)
{
return rendererBlocks != null &&
rendererBlocks.HasPasses(block);
}
protected virtual bool SupportsRendererStage(
RendererRecordingContext context)
{
if (context == null)
{
return false;
}
return HasRendererStageBlocks(context.stage);
}
private bool HasRendererStageBlocks(
CameraFrameStage stage)
{
return HasRendererStageBlocks(
stage,
m_rendererBlocks);
}
private static bool HasRendererStageBlocks(
CameraFrameStage stage,
RendererBlocks rendererBlocks)
{
switch (stage)
{
case CameraFrameStage.ShadowCaster:
return HasRendererBlock(
rendererBlocks,
RendererBlock.ShadowCaster);
case CameraFrameStage.DepthOnly:
return HasRendererBlock(
rendererBlocks,
RendererBlock.DepthPrepass);
case CameraFrameStage.MainScene:
return HasRendererBlock(
rendererBlocks,
RendererBlock.MainOpaque) ||
HasRendererBlock(
rendererBlocks,
RendererBlock.MainSkybox) ||
HasRendererBlock(
rendererBlocks,
RendererBlock.MainTransparent);
case CameraFrameStage.PostProcess:
return HasRendererBlock(
rendererBlocks,
RendererBlock.PostProcess);
case CameraFrameStage.FinalOutput:
return HasRendererBlock(
rendererBlocks,
RendererBlock.FinalOutput);
default:
return false;
}
}
private RendererFramePlan BuildRendererFramePlan(
ScriptableRenderPipelinePlanningContext context)
{
bool finalColorRequiresProcessing =
context.HasFinalColorProcessing();
int rendererIndex = context.rendererIndex;
ulong framePlanId = context.framePlanId;
BuildPassQueue(
RenderingData.CreatePlanning(
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
List<ScriptableRenderPass> activePassQueue =
SnapshotActivePassQueue(
m_activePassQueue);
RendererBlocks rendererBlocks =
new RendererBlocks();
rendererBlocks.Build(activePassQueue);
RendererFramePlan framePlan =
new RendererFramePlan(
framePlanId,
rendererIndex,
activePassQueue);
framePlan.manifest = new PassQueueStageManifest
{
shadowCaster = HasRendererStageBlocks(
CameraFrameStage.ShadowCaster,
rendererBlocks),
depthOnly = HasRendererStageBlocks(
CameraFrameStage.DepthOnly,
rendererBlocks),
postProcess = HasRendererStageBlocks(
CameraFrameStage.PostProcess,
rendererBlocks),
finalOutput = HasRendererStageBlocks(
CameraFrameStage.FinalOutput,
rendererBlocks)
};
return framePlan;
}
private static void ApplyPassQueueStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (manifest.shadowCaster)
{
context.RequestShadowCasterStage();
}
else
{
context.ClearShadowCasterStage();
}
if (manifest.depthOnly)
{
context.RequestCameraDepthOnlyStage();
}
else
{
context.ClearDepthOnlyStage();
}
ApplyPostProcessStageManifest(context, manifest);
ApplyFinalOutputStageManifest(context, manifest);
}
private static void ApplyPostProcessStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.postProcess)
{
context.ClearFullscreenStage(
CameraFrameStage.PostProcess);
return;
}
bool finalOutputReadsPostProcess =
ShouldFinalOutputReadPostProcess(
context,
manifest);
if (!context.IsStageRequested(
CameraFrameStage.PostProcess))
{
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
CameraFrameColorSource.MainSceneColor,
finalOutputReadsPostProcess);
return;
}
if (finalOutputReadsPostProcess &&
!context.UsesGraphManagedOutputColor(
CameraFrameStage.PostProcess))
{
CameraFrameColorSource source =
context.GetStageColorSource(
CameraFrameStage.PostProcess);
context.ClearFullscreenStage(
CameraFrameStage.PostProcess);
context.RequestFullscreenStage(
CameraFrameStage.PostProcess,
source,
true);
}
}
private static bool ShouldFinalOutputReadPostProcess(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.finalOutput ||
!manifest.postProcess)
{
return false;
}
return !context.IsStageRequested(
CameraFrameStage.PostProcess) ||
context.GetStageColorSource(
CameraFrameStage.PostProcess) !=
CameraFrameColorSource.ExplicitSurface;
}
private static void ApplyFinalOutputStageManifest(
ScriptableRenderPipelinePlanningContext context,
PassQueueStageManifest manifest)
{
if (!manifest.finalOutput)
{
context.ClearFullscreenStage(
CameraFrameStage.FinalOutput);
return;
}
if (context.IsStageRequested(
CameraFrameStage.FinalOutput) &&
context.GetStageColorSource(
CameraFrameStage.FinalOutput) ==
CameraFrameColorSource.ExplicitSurface)
{
return;
}
context.ClearFullscreenStage(
CameraFrameStage.FinalOutput);
context.RequestFullscreenStage(
CameraFrameStage.FinalOutput,
ResolveFinalOutputSource(context));
}
private static CameraFrameColorSource ResolveFinalOutputSource(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null ||
!context.IsStageRequested(
CameraFrameStage.PostProcess))
{
return CameraFrameColorSource.MainSceneColor;
}
return context.GetStageColorSource(
CameraFrameStage.PostProcess) ==
CameraFrameColorSource.ExplicitSurface
? CameraFrameColorSource.MainSceneColor
: CameraFrameColorSource.PostProcessColor;
}
protected virtual bool RecordRendererStage(
RendererRecordingContext context,
RenderGraph renderGraph,
ContextContainer frameData)
{
if (context == null ||
context.renderContext == null)
{
return false;
}
bool recordedAnyPass = false;
switch (context.stage)
{
case CameraFrameStage.ShadowCaster:
return RecordRendererBlock(
RendererBlock.ShadowCaster,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
recordedAnyPass;
case CameraFrameStage.DepthOnly:
return RecordRendererBlock(
RendererBlock.DepthPrepass,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
recordedAnyPass;
case CameraFrameStage.MainScene:
return RecordRendererBlock(
RendererBlock.MainOpaque,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
RecordRendererBlock(
RendererBlock.MainSkybox,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
RecordRendererBlock(
RendererBlock.MainTransparent,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
recordedAnyPass;
case CameraFrameStage.PostProcess:
return RecordRendererBlock(
RendererBlock.PostProcess,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
recordedAnyPass;
case CameraFrameStage.FinalOutput:
return RecordRendererBlock(
RendererBlock.FinalOutput,
context,
renderGraph,
frameData,
ref recordedAnyPass) &&
recordedAnyPass;
default:
return false;
}
}
private bool RecordRendererBlock(
RendererBlock block,
RendererRecordingContext context,
RenderGraph renderGraph,
ContextContainer frameData,
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,
renderGraph,
frameData))
{
return false;
}
recordedAnyPass = true;
}
return true;
}
private void StoreRendererFramePlan(
RendererFramePlan framePlan)
{
if (framePlan == null ||
framePlan.framePlanId == 0UL)
{
return;
}
if (!m_framePlans.ContainsKey(
framePlan.framePlanId) &&
m_framePlans.Count >=
kMaxFramePlanSnapshotCount)
{
ulong oldestFramePlanId = 0UL;
bool hasOldestFramePlanId = false;
foreach (ulong framePlanId in
m_framePlans.Keys)
{
oldestFramePlanId = framePlanId;
hasOldestFramePlanId = true;
break;
}
if (hasOldestFramePlanId)
{
m_framePlans.Remove(
oldestFramePlanId);
}
}
m_framePlans[framePlan.framePlanId] =
framePlan;
}
private bool TryGetRendererFramePlan(
RendererRecordingContext context,
out RendererFramePlan framePlan)
{
framePlan = null;
if (context == null ||
context.framePlanId == 0UL)
{
return false;
}
if (!m_framePlans.TryGetValue(
context.framePlanId,
out framePlan) ||
framePlan == null ||
framePlan.rendererIndex !=
context.rendererIndex)
{
return false;
}
return true;
}
private bool SupportsRendererRecordingFromFramePlan(
RendererRecordingContext context,
RendererFramePlan framePlan)
{
LoadRendererFramePlan(framePlan);
try
{
return SupportsRendererStage(context);
}
finally
{
ClearPassQueue();
}
}
private bool RecordRendererFromFramePlan(
RendererRecordingContext context,
RendererFramePlan framePlan)
{
LoadRendererFramePlan(framePlan);
try
{
bool recorded;
using (ContextContainer frameData =
ScriptableRenderPass.BuildRenderGraphFrameData(
context.renderingData))
{
recorded =
RecordRendererStage(
context,
new RenderGraph(context.renderContext),
frameData);
}
if (recorded)
{
FinishCameraStackRendering();
}
return recorded;
}
finally
{
ClearPassQueue();
}
}
private void LoadRendererFramePlan(
RendererFramePlan framePlan)
{
ClearPassQueue();
if (framePlan == null ||
framePlan.activePassQueue == null)
{
return;
}
m_activePassQueue.AddRange(
framePlan.activePassQueue);
m_rendererBlocks.Build(m_activePassQueue);
}
private static List<ScriptableRenderPass> SnapshotActivePassQueue(
IList<ScriptableRenderPass> activePassQueue)
{
List<ScriptableRenderPass> snapshot =
new List<ScriptableRenderPass>();
if (activePassQueue == null)
{
return snapshot;
}
for (int i = 0; i < activePassQueue.Count; ++i)
{
ScriptableRenderPass renderPass =
activePassQueue[i];
if (renderPass == null)
{
continue;
}
snapshot.Add(
renderPass.CreateFramePlanSnapshot());
}
return snapshot;
}
private void BuildPassQueue(
RenderingData renderingData)
{
ClearPassQueue();
for (int i = 0; i < m_features.Count; ++i)
{
ScriptableRendererFeature feature = m_features[i];
if (feature == null || !feature.isActive)
{
continue;
}
feature.SetupRenderPasses(
this,
in renderingData);
feature.AddRenderPasses(
this,
ref renderingData);
}
AddRenderPasses(renderingData);
m_rendererBlocks.Build(m_activePassQueue);
}
private void ClearPassQueue()
{
m_activePassQueue.Clear();
m_rendererBlocks.Clear();
}
private void FinishCameraStackRendering()
{
for (int i = 0; i < m_activePassQueue.Count; ++i)
{
ScriptableRenderPass renderPass =
m_activePassQueue[i];
if (renderPass == null)
{
continue;
}
renderPass.OnFinishCameraStackRendering(
m_finishCameraStackCommandBuffer);
}
}
protected virtual void ReleaseRuntimeResources()
{
}
}
}