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

1021 lines
32 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 PassQueueStageSnapshot
{
public PassQueueStageSnapshot(
CameraFrameStage stage,
List<ScriptableRenderPass> activePassQueue,
RendererBlocks rendererBlocks)
{
this.stage = stage;
this.activePassQueue =
activePassQueue ??
new List<ScriptableRenderPass>();
this.rendererBlocks =
rendererBlocks ?? new RendererBlocks();
}
public readonly CameraFrameStage stage;
public readonly List<ScriptableRenderPass> activePassQueue;
public readonly RendererBlocks rendererBlocks;
}
private sealed class PassQueueFrameSnapshot
{
private readonly Dictionary<
CameraFrameStage,
PassQueueStageSnapshot> m_stageSnapshots =
new Dictionary<
CameraFrameStage,
PassQueueStageSnapshot>();
public PassQueueFrameSnapshot(
ulong framePlanId,
int rendererIndex)
{
this.framePlanId = framePlanId;
this.rendererIndex = rendererIndex;
}
public readonly ulong framePlanId;
public readonly int rendererIndex;
public PassQueueStageManifest manifest;
public void SetStageSnapshot(
PassQueueStageSnapshot snapshot)
{
if (snapshot == null)
{
return;
}
m_stageSnapshots[snapshot.stage] = snapshot;
}
public bool TryGetStageSnapshot(
CameraFrameStage stage,
out PassQueueStageSnapshot snapshot)
{
return m_stageSnapshots.TryGetValue(
stage,
out snapshot);
}
}
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, PassQueueFrameSnapshot>
m_framePlanSnapshots =
new Dictionary<ulong, PassQueueFrameSnapshot>();
private readonly CommandBuffer m_finishCameraStackCommandBuffer =
new CommandBuffer("ScriptableRenderer.FinishCameraStack");
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_framePlanSnapshots.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);
ConfigureRendererFeaturesCameraFramePlan(context);
}
internal void ConfigurePassQueueCameraFramePlanInstance(
ScriptableRenderPipelinePlanningContext context)
{
if (context == null)
{
return;
}
PassQueueFrameSnapshot snapshot =
BuildPassQueueFrameSnapshot(context);
ApplyPassQueueStageManifest(context, snapshot.manifest);
StorePassQueueFrameSnapshot(snapshot);
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;
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
context,
out snapshot))
{
return SupportsRendererRecordingFromSnapshot(
context,
snapshot);
}
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;
PassQueueStageSnapshot snapshot;
if (TryGetPassQueueStageSnapshot(
context,
out snapshot))
{
return RecordRendererFromSnapshot(
context,
snapshot);
}
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 PassQueueFrameSnapshot BuildPassQueueFrameSnapshot(
ScriptableRenderPipelinePlanningContext context)
{
bool finalColorRequiresProcessing =
context.HasFinalColorProcessing();
int rendererIndex = context.rendererIndex;
ulong framePlanId = context.framePlanId;
PassQueueFrameSnapshot snapshot =
new PassQueueFrameSnapshot(
framePlanId,
rendererIndex);
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.ShadowCaster,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.DepthOnly,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.MainScene,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.PostProcess,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
snapshot.SetStageSnapshot(
BuildPassQueueStageSnapshot(
CameraFrameStage.FinalOutput,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
PassQueueStageSnapshot stageSnapshot;
snapshot.manifest = new PassQueueStageManifest
{
shadowCaster =
snapshot.TryGetStageSnapshot(
CameraFrameStage.ShadowCaster,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.ShadowCaster,
stageSnapshot.rendererBlocks),
depthOnly =
snapshot.TryGetStageSnapshot(
CameraFrameStage.DepthOnly,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.DepthOnly,
stageSnapshot.rendererBlocks),
postProcess =
snapshot.TryGetStageSnapshot(
CameraFrameStage.PostProcess,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.PostProcess,
stageSnapshot.rendererBlocks),
finalOutput =
snapshot.TryGetStageSnapshot(
CameraFrameStage.FinalOutput,
out stageSnapshot) &&
HasRendererStageBlocks(
CameraFrameStage.FinalOutput,
stageSnapshot.rendererBlocks)
};
return snapshot;
}
private PassQueueStageSnapshot BuildPassQueueStageSnapshot(
CameraFrameStage stage,
int rendererIndex,
bool finalColorRequiresProcessing,
ulong framePlanId)
{
BuildPassQueue(
RenderingData.CreatePlanning(
stage,
rendererIndex,
finalColorRequiresProcessing,
framePlanId));
List<ScriptableRenderPass> activePassQueue =
new List<ScriptableRenderPass>(
m_activePassQueue);
RendererBlocks rendererBlocks =
new RendererBlocks();
rendererBlocks.Build(activePassQueue);
return new PassQueueStageSnapshot(
stage,
activePassQueue,
rendererBlocks);
}
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 StorePassQueueFrameSnapshot(
PassQueueFrameSnapshot snapshot)
{
if (snapshot == null ||
snapshot.framePlanId == 0UL)
{
return;
}
if (!m_framePlanSnapshots.ContainsKey(
snapshot.framePlanId) &&
m_framePlanSnapshots.Count >=
kMaxFramePlanSnapshotCount)
{
ulong oldestFramePlanId = 0UL;
bool hasOldestFramePlanId = false;
foreach (ulong framePlanId in
m_framePlanSnapshots.Keys)
{
oldestFramePlanId = framePlanId;
hasOldestFramePlanId = true;
break;
}
if (hasOldestFramePlanId)
{
m_framePlanSnapshots.Remove(
oldestFramePlanId);
}
}
m_framePlanSnapshots[snapshot.framePlanId] =
snapshot;
}
private bool TryGetPassQueueStageSnapshot(
RendererRecordingContext context,
out PassQueueStageSnapshot stageSnapshot)
{
stageSnapshot = null;
if (context == null ||
context.framePlanId == 0UL)
{
return false;
}
PassQueueFrameSnapshot frameSnapshot;
if (!m_framePlanSnapshots.TryGetValue(
context.framePlanId,
out frameSnapshot) ||
frameSnapshot == null ||
frameSnapshot.rendererIndex !=
context.rendererIndex)
{
return false;
}
return frameSnapshot.TryGetStageSnapshot(
context.stage,
out stageSnapshot);
}
private bool SupportsRendererRecordingFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
try
{
return SupportsRendererStage(context);
}
finally
{
ClearPassQueue();
}
}
private bool RecordRendererFromSnapshot(
RendererRecordingContext context,
PassQueueStageSnapshot snapshot)
{
LoadPassQueueStageSnapshot(snapshot);
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 LoadPassQueueStageSnapshot(
PassQueueStageSnapshot snapshot)
{
ClearPassQueue();
if (snapshot == null ||
snapshot.activePassQueue == null)
{
return;
}
m_activePassQueue.AddRange(
snapshot.activePassQueue);
m_rendererBlocks.Build(m_activePassQueue);
}
private void BuildPassQueue(
RenderingData renderingData)
{
ClearPassQueue();
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.SetupRenderPasses(
this,
in renderingData);
feature.AddRenderPasses(
this,
ref renderingData);
}
AddRenderPasses(renderingData);
m_rendererBlocks.Build(m_activePassQueue);
}
finally
{
m_isBuildingPassQueue = false;
m_passQueueStage =
CameraFrameStage.MainScene;
}
}
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()
{
}
}
}