141 lines
7.5 KiB
C#
141 lines
7.5 KiB
C#
|
using UnityEngine;
|
||
|
using UnityEngine.Rendering;
|
||
|
using UnityEngine.Rendering.RenderGraphModule;
|
||
|
using UnityEngine.Rendering.RenderGraphModule.Util;
|
||
|
using UnityEngine.Rendering.Universal;
|
||
|
|
||
|
// This example copies the target of the previous pass to a new texture using a custom material and framebuffer fetch. This example is for API demonstrative purposes,
|
||
|
// so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents.
|
||
|
|
||
|
// Framebuffer fetch: this is an advanced TBDR GPU optimization that allows subpasses to read the output of previous subpasses directly from the framebuffer,
|
||
|
// greatly reducing the bandwidth usage.
|
||
|
public class FrameBufferFetchRenderFeature : ScriptableRendererFeature
|
||
|
{
|
||
|
class FrameBufferFetchPass : ScriptableRenderPass
|
||
|
{
|
||
|
private Material m_BlitMaterial;
|
||
|
private Material m_FBFetchMaterial;
|
||
|
|
||
|
public FrameBufferFetchPass( Material fbFetchMaterial)
|
||
|
{
|
||
|
m_FBFetchMaterial = fbFetchMaterial;
|
||
|
|
||
|
//The pass will read the current color texture. That needs to be an intermediate texture. It's not supported to use the BackBuffer as input texture.
|
||
|
//By setting this property, URP will automatically create an intermediate texture. This has a performance cost so don't set this if you don't need it.
|
||
|
//It's good practice to set it here and not from the RenderFeature. This way, the pass is selfcontaining and you can use it to directly enqueue the pass from a monobehaviour without a RenderFeature.
|
||
|
requiresIntermediateTexture = true;
|
||
|
}
|
||
|
|
||
|
// This class stores the data needed by the pass, passed as parameter to the delegate function that executes the pass
|
||
|
private class PassData
|
||
|
{
|
||
|
internal TextureHandle src;
|
||
|
internal Material material;
|
||
|
internal bool useMSAA;
|
||
|
}
|
||
|
|
||
|
// This static method is used to execute the pass and passed as the RenderFunc delegate to the RenderGraph render pass
|
||
|
static void ExecuteFBFetchPass(PassData data, RasterGraphContext context)
|
||
|
{
|
||
|
context.cmd.DrawProcedural(Matrix4x4.identity, data.material, data.useMSAA? 1 : 0, MeshTopology.Triangles, 3, 1, null);
|
||
|
}
|
||
|
|
||
|
private void FBFetchPass(RenderGraph renderGraph, ContextContainer frameData, TextureHandle source, TextureHandle destination, bool useMSAA)
|
||
|
{
|
||
|
string passName = "FrameBufferFetchPass";
|
||
|
|
||
|
// This simple pass copies the target of the previous pass to a new texture using a custom material and framebuffer fetch. This sample is for API demonstrative purposes,
|
||
|
// so the new texture is not used anywhere else in the frame, you can use the frame debugger to verify its contents.
|
||
|
|
||
|
// add a raster render pass to the render graph, specifying the name and the data type that will be passed to the ExecutePass function
|
||
|
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData))
|
||
|
{
|
||
|
// Fill the pass data
|
||
|
passData.material = m_FBFetchMaterial;
|
||
|
passData.useMSAA = useMSAA;
|
||
|
|
||
|
// We declare the src as input attachment. This is required for Frame buffer fetch.
|
||
|
builder.SetInputAttachment(source, 0, AccessFlags.Read);
|
||
|
|
||
|
// Setup as a render target via UseTextureFragment, which is the equivalent of using the old cmd.SetRenderTarget
|
||
|
builder.SetRenderAttachment(destination, 0);
|
||
|
|
||
|
// We disable culling for this pass for the demonstrative purpose of this sample, as normally this pass would be culled,
|
||
|
// since the destination texture is not used anywhere else
|
||
|
builder.AllowPassCulling(false);
|
||
|
|
||
|
// Assign the ExecutePass function to the render pass delegate, which will be called by the render graph when executing the pass
|
||
|
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecuteFBFetchPass(data, context));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This is where the renderGraph handle can be accessed.
|
||
|
// Each ScriptableRenderPass can use the RenderGraph handle to add multiple render passes to the render graph
|
||
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||
|
{
|
||
|
// This pass showcases how to implement framebuffer fetch: this is an advanced TBDR GPU optimization
|
||
|
// that allows subpasses to read the output of previous subpasses directly from the framebuffer, reducing greatly the bandwidth usage.
|
||
|
// The first pass BlitPass simply copies the Camera Color in a temporary render target, the second pass FBFetchPass copies the temporary render target
|
||
|
// to another render target using framebuffer fetch.
|
||
|
// As a result, the passes are merged (you can verify in the RenderGraph Visualizer) and the bandwidth usage is reduced, since we can discard the temporary render target.
|
||
|
|
||
|
|
||
|
// UniversalResourceData contains all the texture handles used by the renderer, including the active color and depth textures
|
||
|
// The active color and depth textures are the main color and depth buffers that the camera renders into
|
||
|
var resourceData = frameData.Get<UniversalResourceData>();
|
||
|
|
||
|
// The destination texture is created here,
|
||
|
// the texture is created with the same dimensions as the active color texture
|
||
|
var source = resourceData.activeColorTexture;
|
||
|
|
||
|
var destinationDesc = renderGraph.GetTextureDesc(source);
|
||
|
destinationDesc.name = "FBFetchDestTexture";
|
||
|
destinationDesc.clearBuffer = false;
|
||
|
|
||
|
if (destinationDesc.msaaSamples == MSAASamples.None || RenderGraphUtils.CanAddCopyPassMSAA())
|
||
|
{
|
||
|
TextureHandle fbFetchDestination = renderGraph.CreateTexture(destinationDesc);
|
||
|
|
||
|
FBFetchPass(renderGraph, frameData, source, fbFetchDestination, destinationDesc.msaaSamples != MSAASamples.None);
|
||
|
|
||
|
//Copy back the FBF output to the camera color to easily see the result in the game view
|
||
|
//This copy pass also uses FBF under the hood. All the passes should be merged this way and the destination attachment should be memoryless (no load/store of memory).
|
||
|
renderGraph.AddCopyPass(fbFetchDestination, source, passName: "Copy Back FF Destination (also using FBF)");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.Log("Can't add the FBF pass and the copy pass due to MSAA");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
FrameBufferFetchPass m_FbFetchPass;
|
||
|
|
||
|
public Material m_FBFetchMaterial;
|
||
|
|
||
|
/// <inheritdoc/>
|
||
|
public override void Create()
|
||
|
{
|
||
|
m_FbFetchPass = new FrameBufferFetchPass(m_FBFetchMaterial);
|
||
|
|
||
|
// Configures where the render pass should be injected.
|
||
|
m_FbFetchPass.renderPassEvent = RenderPassEvent.BeforeRenderingTransparents;
|
||
|
}
|
||
|
|
||
|
// Here you can inject one or multiple render passes in the renderer.
|
||
|
// This method is called when setting up the renderer once per-camera.
|
||
|
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||
|
{
|
||
|
// Early exit if there are no materials.
|
||
|
if (m_FBFetchMaterial == null)
|
||
|
{
|
||
|
Debug.LogWarning( this.name + " material is null and will be skipped.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
renderer.EnqueuePass(m_FbFetchPass);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|