701 lines
22 KiB
C#
701 lines
22 KiB
C#
using System.Collections.Generic;
|
|
using XCEngine;
|
|
using XCEngine.Rendering;
|
|
using XCEngine.Rendering.RenderGraphModule;
|
|
|
|
namespace XCEngine.Rendering.Universal
|
|
{
|
|
public abstract class ScriptableRenderer
|
|
{
|
|
private struct PassQueueStageManifest
|
|
{
|
|
public bool shadowCaster;
|
|
public bool depthOnly;
|
|
public bool postProcess;
|
|
public bool finalOutput;
|
|
}
|
|
|
|
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 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_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;
|
|
}
|
|
|
|
PassQueueStageManifest manifest =
|
|
BuildPassQueueStageManifest(context);
|
|
ApplyPassQueueStageManifest(context, manifest);
|
|
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;
|
|
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);
|
|
bool recorded;
|
|
using (ContextContainer frameData =
|
|
ScriptableRenderPass.BuildRenderGraphFrameData(
|
|
renderingData))
|
|
{
|
|
recorded =
|
|
RecordRendererStage(
|
|
context,
|
|
new RenderGraph(context.renderContext),
|
|
frameData);
|
|
}
|
|
if (recorded)
|
|
{
|
|
FinishCameraStackRendering();
|
|
}
|
|
|
|
return recorded;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
protected virtual bool SupportsRendererStage(
|
|
RendererRecordingContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return HasRendererStageBlocks(context.stage);
|
|
}
|
|
|
|
private bool HasRendererStageBlocks(
|
|
CameraFrameStage stage)
|
|
{
|
|
switch (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;
|
|
}
|
|
}
|
|
|
|
private PassQueueStageManifest BuildPassQueueStageManifest(
|
|
ScriptableRenderPipelinePlanningContext context)
|
|
{
|
|
bool finalColorRequiresProcessing =
|
|
context.HasFinalColorProcessing();
|
|
int rendererIndex = context.rendererIndex;
|
|
|
|
return new PassQueueStageManifest
|
|
{
|
|
shadowCaster =
|
|
HasPassQueueStage(
|
|
CameraFrameStage.ShadowCaster,
|
|
rendererIndex,
|
|
finalColorRequiresProcessing),
|
|
depthOnly =
|
|
HasPassQueueStage(
|
|
CameraFrameStage.DepthOnly,
|
|
rendererIndex,
|
|
finalColorRequiresProcessing),
|
|
postProcess =
|
|
HasPassQueueStage(
|
|
CameraFrameStage.PostProcess,
|
|
rendererIndex,
|
|
finalColorRequiresProcessing),
|
|
finalOutput =
|
|
HasPassQueueStage(
|
|
CameraFrameStage.FinalOutput,
|
|
rendererIndex,
|
|
finalColorRequiresProcessing)
|
|
};
|
|
}
|
|
|
|
private bool HasPassQueueStage(
|
|
CameraFrameStage stage,
|
|
int rendererIndex,
|
|
bool finalColorRequiresProcessing)
|
|
{
|
|
BuildPassQueue(
|
|
RenderingData.CreatePlanning(
|
|
stage,
|
|
rendererIndex,
|
|
finalColorRequiresProcessing));
|
|
return HasRendererStageBlocks(stage);
|
|
}
|
|
|
|
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 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()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|