Rasagar/Library/PackageCache/com.unity.render-pipelines.universal/Samples~/URPPackageSamples/RendererFeatures/KeepFrame/KeepFrameFeature.cs

227 lines
9.8 KiB
C#
Raw Normal View History

2024-08-26 13:07:20 -07:00
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// This renderer feature will replicate a "don't clear" behaviour by injecting two passes into the pipeline:
// One pass that copies color at the end of a frame
// Another pass that draws the content of the copied texture at the beginning of a new frame
// In this version of the sample we provide implementations for both RenderGraph and non-RenderGraph pipelines.
// This way you can easily see what changed and how to manage code bases with backwards compatibility
public class KeepFrameFeature : ScriptableRendererFeature
{
// This pass is responsible for copying color to a specified destination
class CopyFramePass : ScriptableRenderPass
{
class PassData
{
public TextureHandle source;
}
RTHandle m_Destination;
public void Setup(RTHandle destination)
{
m_Destination = destination;
}
#pragma warning disable 618, 672 // Type or member is obsolete, Member overrides obsolete member
// Unity calls the Execute method in the Compatibility mode
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (renderingData.cameraData.camera.cameraType != CameraType.Game)
return;
var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
CommandBuffer cmd = CommandBufferPool.Get("CopyFramePass");
Blit(cmd, source, m_Destination);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#pragma warning restore 618, 672
// RecordRenderGraph is called for the RenderGraph path.
// Because RenderGraph has to calculate internally how resources are used we must be aware of 2
// distinct timelines inside this method: one for recording resource usage and one for recording draw commands.
// It is important to scope resources correctly as global state may change between the execution times of each.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
if (cameraData.camera.cameraType != CameraType.Game)
return;
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Copy Frame Pass", out var passData))
{
TextureHandle source = resourceData.activeColorTexture;
// When using the RenderGraph API the lifetime and ownership of resources is managed by the render graph system itself.
// This allows for optimal resource usage and other optimizations to be done automatically for the user.
// In the cases where resources must persist across frames, between different cameras or when users want
// to manage their lifetimes themselves, the resources must be imported when recording the render pass.
TextureHandle destination = renderGraph.ImportTexture(m_Destination);
if (!source.IsValid() || !destination.IsValid())
return;
passData.source = source;
builder.UseTexture(source, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
Blitter.BlitTexture(context.cmd, data.source, new Vector4(1, 1, 0, 0), 0, true);
});
}
}
}
// This pass is responsible for drawing the old color to a full screen quad
class DrawOldFramePass : ScriptableRenderPass
{
class PassData
{
public TextureHandle source;
public Material material;
public string name;
}
Material m_DrawOldFrameMaterial;
RTHandle m_Handle;
string m_TextureName;
public void Setup(Material drawOldFrameMaterial, RTHandle handle, string textureName)
{
m_DrawOldFrameMaterial = drawOldFrameMaterial;
m_TextureName = textureName;
m_Handle = handle;
}
// This is an example of how to share code between RenderGraph and older non-RenderGraph setups.
// The common draw commands are extracted in a private static method that gets called from both
// Execute and render graph builder's SetRenderFunc.
static void ExecutePass(RasterCommandBuffer cmd, RTHandle source, Material material)
{
if (material == null)
return;
Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
Blitter.BlitTexture(cmd, source, viewportScale, material, 0);
}
#pragma warning disable 618, 672 // Type or member is obsolete, Member overrides obsolete member
// Unity calls the Execute method in the Compatibility mode
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get(nameof(DrawOldFramePass));
cmd.SetGlobalTexture(m_TextureName, m_Handle);
var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(cmd), source, m_DrawOldFrameMaterial);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#pragma warning restore 618, 672
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
TextureHandle oldFrameTextureHandle = renderGraph.ImportTexture(m_Handle);
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Draw Old Frame Pass", out var passData))
{
TextureHandle destination = resourceData.activeColorTexture;
if (!oldFrameTextureHandle.IsValid() || !destination.IsValid())
return;
passData.material = m_DrawOldFrameMaterial;
passData.source = oldFrameTextureHandle;
passData.name = m_TextureName;
builder.UseTexture(oldFrameTextureHandle, AccessFlags.Read);
builder.SetRenderAttachment(destination, 0, AccessFlags.Write);
// Normally global state modifications are not allowed when using RenderGraph and will result in errors.
// In the exceptional cases where this is intentional we must let the RenderGraph API know by calling
// AllowGlobalStateModification(true). Use this only where necessary as it will introduce a sync point
// in the frame which may have a negative impact on performance.
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
context.cmd.SetGlobalTexture(data.name, data.source);
ExecutePass(context.cmd, data.source, data.material);
});
}
}
}
[Serializable]
public class Settings
{
[Tooltip("The material that is used when the old frame is redrawn at the start of the new frame (before opaques).")]
public Material displayMaterial;
[Tooltip("The name of the texture used for referencing the copied frame. (Defaults to _FrameCopyTex if empty)")]
public string textureName;
}
CopyFramePass m_CopyFrame;
DrawOldFramePass m_DrawOldFrame;
RTHandle m_OldFrameHandle;
public Settings settings = new Settings();
// In this function the passes are created and their point of injection is set
public override void Create()
{
m_CopyFrame = new CopyFramePass();
m_CopyFrame.renderPassEvent = RenderPassEvent.AfterRenderingTransparents; // Frame color is copied late in the frame
m_DrawOldFrame = new DrawOldFramePass();
m_DrawOldFrame.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques; // Old frame is drawn early in the frame
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
var descriptor = renderingData.cameraData.cameraTargetDescriptor;
descriptor.msaaSamples = 1;
descriptor.depthBufferBits = 0;
descriptor.graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB;
var textureName = String.IsNullOrEmpty(settings.textureName) ? "_FrameCopyTex" : settings.textureName;
RenderingUtils.ReAllocateHandleIfNeeded(ref m_OldFrameHandle, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: textureName);
m_CopyFrame.Setup(m_OldFrameHandle);
m_DrawOldFrame.Setup(settings.displayMaterial, m_OldFrameHandle, textureName);
renderer.EnqueuePass(m_CopyFrame);
renderer.EnqueuePass(m_DrawOldFrame);
}
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
// This path is not taken when using render graph.
// The code to reallocate m_OldFrameHandle has been moved to AddRenderPasses in order to avoid duplication.
}
protected override void Dispose(bool disposing)
{
m_OldFrameHandle?.Release();
}
}