Files
2025-05-15 15:37:45 +08:00

474 lines
21 KiB
C#

#if UNITY_6000_0_OR_NEWER
#define UNITY_RENDER_GRAPH
#else
#define UNITY_LEGACY
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
#if UNITY_RENDER_GRAPH
using UnityEngine.Rendering.RenderGraphModule;
#endif
// VolFx © NullTale - https://x.com/NullTale
namespace VolFx.Tools
{
[DisallowMultipleRendererFeature("VolFx Pool")]
public class VolFxPool : ScriptableRendererFeature
{
private static readonly int s_CameraDepthAttachment = Shader.PropertyToID("_CameraDepthAttachment");
private static List<ShaderTagId> k_ShaderTags;
private static List<PoolPass> s_LayersExternal = new List<PoolPass>();
public SoCollection<Pool> _list = new SoCollection<Pool>();
private List<PoolPass> _bufferPasses = new List<PoolPass>();
private static Material _blit;
internal static Material _adj;
// =======================================================================
private class PoolPass : ScriptableRenderPass
{
public Pool _pool;
private RenderTarget _output;
private RenderTarget _depth;
private RendererListParams _rlp;
private ProfilingSampler _profiler;
private int _layerMask;
// =======================================================================
#if UNITY_RENDER_GRAPH
public class PassData
{
public TextureHandle _output;
public TextureHandle _camDepth;
public TextureHandle _passDepth;
public RendererListHandle _rl;
}
#endif
// =======================================================================
public void Init()
{
renderPassEvent = _pool._event;
_output = new RenderTarget() { Handle = null, Id = Shader.PropertyToID(_pool.GlobalTexName) };
_depth = new RenderTarget().Allocate($"{_pool.GlobalTexName}_Depth");
_profiler = new ProfilingSampler(_pool.name);
_initRenderList();
//_pool._poolTex = new RenderTexture(Screen.width, Screen.height, GraphicsFormat.R8G8B8A8_SRGB, GraphicsFormat.None);
if (SystemInfo.copyTextureSupport == CopyTextureSupport.None && _pool._depth == Pool.DepthStencil.Copy)
Debug.LogWarning($"\'{_pool.name}\' buffer depth mode is set to copy, but texture copy functionality is not supported by the platform");
_layerMask = _pool._mask.value;
}
#if UNITY_RENDER_GRAPH
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var rndData = frameData.Get<UniversalRenderingData>();
var resData = frameData.Get<UniversalResourceData>();
var camData = frameData.Get<UniversalCameraData>();
var lghData = frameData.Get<UniversalLightData>();
ref var camRtDesc = ref camData.cameraTargetDescriptor;
using (var builder = renderGraph.AddUnsafePass<PassData>(passName, out var passData, _profiler))
{
//var tex = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, _pool.GlobalTexName, false, FilterMode.Point, TextureWrapMode.Repeat);
var tex = renderGraph.ImportTexture(_pool.GetHandle(camRtDesc.width, camRtDesc.height));
// Shader.SetGlobalTexture(_pool.GlobalTexName, _pool.GetHandle(camRtDesc.width, camRtDesc.height));
passData._output = tex;
passData._camDepth = resData.activeDepthTexture;
builder.UseTexture(passData._output, AccessFlags.ReadWrite);
//builder.UseTexture(passData._camDepth, AccessFlags.ReadWrite);
if (_pool._depth == Pool.DepthStencil.Copy || _pool._depth == Pool.DepthStencil.Clean)
{
var desc = new RenderTextureDescriptor(camRtDesc.width, camRtDesc.height, RenderTextureFormat.Depth, 32);
passData._passDepth = UniversalRenderer.CreateRenderGraphTexture(renderGraph, desc, $"{_pool.GlobalTexName}_Depth", false);
builder.UseTexture(passData._passDepth, AccessFlags.ReadWrite);
}
_rlp.cullingResults = rndData.cullResults;
_rlp.drawSettings = RenderingUtils.CreateDrawingSettings(k_ShaderTags, rndData, camData, lghData, SortingCriteria.CommonTransparent);
passData._rl = renderGraph.CreateRendererList(_rlp);
builder.UseRendererList(passData._rl);
builder.AllowPassCulling(false);
builder.SetRenderFunc((PassData data, UnsafeGraphContext context) => Execute(data, context));
builder.SetGlobalTextureAfterPass(passData._output, Shader.PropertyToID(_pool.GlobalTexName));
}
}
internal static MaterialPropertyBlock _mat = new MaterialPropertyBlock();
private void Execute(PassData data, UnsafeGraphContext context)
{
_pool._isLegacy = false;
var cmd = context.cmd;
switch (_pool._depth)
{
case Pool.DepthStencil.None:
cmd.SetRenderTarget(data._output);
break;
case Pool.DepthStencil.Camera:
cmd.SetRenderTarget(data._output, data._camDepth);
break;
case Pool.DepthStencil.Clean:
cmd.SetRenderTarget(data._output, data._passDepth);
break;
case Pool.DepthStencil.Copy:
{
_mat.SetTexture(s_CameraDepthAttachment, data._camDepth);
//cmd.SetRenderTarget(data._output, data._passDepth);
cmd.SetRenderTarget(data._passDepth);
cmd.ClearRenderTarget(RTClearFlags.Depth, clearColor, 1f, 0);
cmd.DrawMesh(Utils.FullscreenMesh, Matrix4x4.identity, _blit, 0, 2, _mat);
cmd.SetRenderTarget(data._output, data._passDepth);
} break;
default:
throw new ArgumentOutOfRangeException();
}
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color, _pool._clear.Value, 1f, 0);
cmd.DrawRendererList(data._rl);
}
[Obsolete("This rendering path is for compatibility mode only (when Render Graph is disabled). Use Render Graph API instead.", false)]
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
var colorDesc = renderingData.cameraData.cameraTargetDescriptor;
if (_pool._format.Enabled)
colorDesc.colorFormat = _pool._format.Value;
_output.Get(cmd, in colorDesc);
}
[Obsolete("This rendering path is for compatibility mode only (when Render Graph is disabled). Use Render Graph API instead.", false)]
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
#if UNITY_EDITOR
if (_pool == null)
return;
#endif
_pool._isLegacy = true;
// allocate resources
var cmd = CommandBufferPool.Get(nameof(VolFxPool));
_profiler.Begin(cmd);
#if UNITY_2022_1_OR_NEWER
var depth = renderingData.cameraData.renderer.cameraDepthTargetHandle;
#else
var depth = renderingData.cameraData.renderer.cameraDepthTarget == BuiltinRenderTextureType.CameraTarget
? renderingData.cameraData.renderer.cameraColorTarget
: renderingData.cameraData.renderer.cameraDepthTarget;
#endif
switch (_pool._depth)
{
case Pool.DepthStencil.None:
cmd.SetRenderTarget(_output.Id);
break;
case Pool.DepthStencil.Copy:
{
var depthDesc = renderingData.cameraData.cameraTargetDescriptor;
depthDesc.colorFormat = RenderTextureFormat.Depth;
depthDesc.graphicsFormat = GraphicsFormat.None;
depthDesc.depthStencilFormat = GraphicsFormat.D32_SFloat;
depthDesc.depthBufferBits = 32;
_depth.Get(cmd, in depthDesc);
if (SystemInfo.copyTextureSupport != CopyTextureSupport.None)
{
cmd.CopyTexture(depth, _depth.Id);
}
else
{
// fix of hope (if api does not support CopyTexture functionality very unlikely it can handle SV_Depth)
cmd.SetGlobalTexture("_CameraDepthAttachment", depth);
Utils.Blit(cmd, _depth, _blit, 2);
}
cmd.SetRenderTarget(_output.Id, _depth.Handle);
} break;
case Pool.DepthStencil.Camera:
cmd.SetRenderTarget(_output.Id, depth);
break;
case Pool.DepthStencil.Clean:
{
var depthDesc = renderingData.cameraData.cameraTargetDescriptor;
depthDesc.colorFormat = RenderTextureFormat.Depth;
depthDesc.graphicsFormat = GraphicsFormat.None;
depthDesc.depthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
_depth.Get(cmd, in depthDesc);
cmd.SetRenderTarget(_output.Id, _depth.Handle);
} break;
default:
throw new ArgumentOutOfRangeException();
}
if (_pool._depth == Pool.DepthStencil.Camera)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.Copy)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.None)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color | RTClearFlags.Depth, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.Clean)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color | RTClearFlags.Depth, _pool._clear, 1f, 0);
else
cmd.ClearRenderTarget(RTClearFlags.Depth, Color.clear, 1f, 0);
}
if (_pool._mask.Enabled && _pool._mask.Value != 0)
{
#if UNITY_EDITOR
// editor validate fix
if (_layerMask != _pool._mask.value)
{
_initRenderList();
_layerMask = _pool._mask.Value;
}
#endif
ref var cameraData = ref renderingData.cameraData;
var camera = cameraData.camera;
camera.TryGetCullingParameters(out var cullingParameters);
_rlp.cullingResults = context.Cull(ref cullingParameters);
_rlp.drawSettings = CreateDrawingSettings(k_ShaderTags, ref renderingData, SortingCriteria.CommonTransparent);
var rl = context.CreateRendererList(ref _rlp);
cmd.DrawRendererList(rl);
}
cmd.SetRenderTarget(renderingData.cameraData.renderer.cameraColorTargetHandle, depth);
_profiler.End(cmd);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#endif
#if UNITY_LEGACY
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
var colorDesc = renderingData.cameraData.cameraTargetDescriptor;
if (_pool._format.Enabled)
colorDesc.colorFormat = _pool._format.Value;
_output.Get(cmd, in colorDesc);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
#if UNITY_EDITOR
if (_pool == null)
return;
#endif
_pool._isLegacy = true;
// allocate resources
var cmd = CommandBufferPool.Get(nameof(VolFxPool));
_profiler.Begin(cmd);
#if UNITY_2022_1_OR_NEWER
var depth = renderingData.cameraData.renderer.cameraDepthTargetHandle;
#else
var depth = renderingData.cameraData.renderer.cameraDepthTarget == BuiltinRenderTextureType.CameraTarget
? renderingData.cameraData.renderer.cameraColorTarget
: renderingData.cameraData.renderer.cameraDepthTarget;
#endif
switch (_pool._depth)
{
case Pool.DepthStencil.None:
cmd.SetRenderTarget(_output.Id);
break;
case Pool.DepthStencil.Copy:
{
var depthDesc = renderingData.cameraData.cameraTargetDescriptor;
depthDesc.colorFormat = RenderTextureFormat.Depth;
depthDesc.graphicsFormat = GraphicsFormat.None;
depthDesc.depthStencilFormat = GraphicsFormat.D32_SFloat;
depthDesc.depthBufferBits = 32;
_depth.Get(cmd, in depthDesc);
if (SystemInfo.copyTextureSupport != CopyTextureSupport.None)
{
cmd.CopyTexture(depth, _depth.Id);
}
else
{
// fix of hope (if api does not support CopyTexture functionality very unlikely it can handle SV_Depth)
cmd.SetGlobalTexture("_CameraDepthAttachment", depth);
Utils.Blit(cmd, _depth, _blit, 2);
}
cmd.SetRenderTarget(_output.Id, _depth.Handle);
} break;
case Pool.DepthStencil.Camera:
cmd.SetRenderTarget(_output.Id, depth);
break;
case Pool.DepthStencil.Clean:
{
var depthDesc = renderingData.cameraData.cameraTargetDescriptor;
depthDesc.colorFormat = RenderTextureFormat.Depth;
depthDesc.graphicsFormat = GraphicsFormat.None;
depthDesc.depthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
_depth.Get(cmd, in depthDesc);
cmd.SetRenderTarget(_output.Id, _depth.Handle);
} break;
default:
throw new ArgumentOutOfRangeException();
}
if (_pool._depth == Pool.DepthStencil.Camera)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.Copy)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.None)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color | RTClearFlags.Depth, _pool._clear, 1f, 0);
}
else
if (_pool._depth == Pool.DepthStencil.Clean)
{
if (_pool._clear.Enabled)
cmd.ClearRenderTarget(RTClearFlags.Color | RTClearFlags.Depth, _pool._clear, 1f, 0);
else
cmd.ClearRenderTarget(RTClearFlags.Depth, Color.clear, 1f, 0);
}
if (_pool._mask.Enabled && _pool._mask.Value != 0)
{
#if UNITY_EDITOR
// editor validate fix
if (_layerMask != _pool._mask.value)
{
_initRenderList();
_layerMask = _pool._mask.Value;
}
#endif
ref var cameraData = ref renderingData.cameraData;
var camera = cameraData.camera;
camera.TryGetCullingParameters(out var cullingParameters);
_rlp.cullingResults = context.Cull(ref cullingParameters);
_rlp.drawSettings = CreateDrawingSettings(k_ShaderTags, ref renderingData, SortingCriteria.CommonTransparent);
var rl = context.CreateRendererList(ref _rlp);
cmd.DrawRendererList(rl);
}
_profiler.End(cmd);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
#endif
private void _initRenderList()
{
_rlp = new RendererListParams(new CullingResults(), new DrawingSettings(), new FilteringSettings(RenderQueueRange.all, _pool._mask.Value));
}
public override void FrameCleanup(CommandBuffer cmd)
{
if (_pool._depth == Pool.DepthStencil.Copy)
_depth.Release(cmd);
if (_pool._depth == Pool.DepthStencil.Clean)
_depth.Release(cmd);
//_output.Release(cmd);
}
}
// =======================================================================
public override void Create()
{
_bufferPasses = _list
.Values
.Select(n => new PoolPass() { _pool = n })
.Where(n => n._pool != null)
.ToList();
if (k_ShaderTags == null)
{
k_ShaderTags = new List<ShaderTagId>(new[]
{
new ShaderTagId("SRPDefaultUnlit"),
new ShaderTagId("UniversalForward"),
new ShaderTagId("UniversalForwardOnly")
});
}
foreach (var pass in _bufferPasses)
pass.Init();
if (_blit == null)
_blit = new Material(Shader.Find("Hidden/VolFx/Blit"));
}
private void OnDestroy()
{
_list.Destroy();
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
// in game view only (ignore inspector draw)
if (renderingData.cameraData.cameraType != CameraType.Game)
return;
foreach (var pass in _bufferPasses)
renderer.EnqueuePass(pass);
foreach (var pass in s_LayersExternal)
renderer.EnqueuePass(pass);
}
}
}