#if SURFACE_CACHE
using System;
using Unity.Mathematics;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.UnifiedRayTracing;
namespace UnityEngine.Rendering
{
internal class SurfaceCacheRingConfig : IDisposable
{
private GraphicsBuffer _buffer; // Stores (count, start, end) twice to allow double buffering.
private uint _flipflop = 0;
public uint OffsetA => GetOffsetA(_flipflop);
public uint OffsetB => GetOffsetB(_flipflop);
public uint FlipFlop => _flipflop;
public GraphicsBuffer Buffer => _buffer;
public SurfaceCacheRingConfig()
{
const int ringBufferElementCount = 3 * 2;
_buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, ringBufferElementCount, sizeof(uint));
_buffer.SetData(new uint[ringBufferElementCount], 0, 0, ringBufferElementCount);
}
public void Flip()
{
_flipflop = Flip(_flipflop);
}
static public uint Flip(uint flip)
{
return flip ^ 1;
}
static public uint GetOffsetA(uint flipflop)
{
return flipflop * 3;
}
static public uint GetOffsetB(uint flipflop)
{
return (flipflop ^ 1) * 3;
}
public void Dispose()
{
_buffer.Dispose();
}
}
internal class SurfaceCachePatchList : IDisposable
{
private readonly uint _capacity;
private GraphicsBuffer _geometries;
private GraphicsBuffer _cellIndices;
private GraphicsBuffer _counterSets;
private GraphicsBuffer[] _irradiances;
private GraphicsBuffer _statistics;
private GraphicsBuffer[] _restirRealizations;
private GraphicsBuffer _risAccumulatedLuminances;
public uint Capacity => _capacity;
public GraphicsBuffer Geometries => _geometries;
public GraphicsBuffer CellIndices => _cellIndices;
public GraphicsBuffer CounterSets => _counterSets;
public GraphicsBuffer[] Irradiances => _irradiances;
public GraphicsBuffer Statistics => _statistics;
public GraphicsBuffer[] RestirRealizations => _restirRealizations;
public GraphicsBuffer RisAccumulatedLuminances => _risAccumulatedLuminances;
public SurfaceCachePatchList(uint capacity, SurfaceCacheEstimationMethod estimationMethod)
{
_capacity = capacity;
int capacityInt = (int)capacity;
const int irradianceStride = sizeof(float) * 12;
_geometries = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(float) * 3 * 2);
_cellIndices = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(uint));
_counterSets = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(uint));
_irradiances = new GraphicsBuffer[3];
_irradiances[0] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride);
_irradiances[1] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride);
_irradiances[2] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride);
_statistics = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(float) * 3 * 2);
if (estimationMethod == SurfaceCacheEstimationMethod.Restir)
{
_restirRealizations = new GraphicsBuffer[2];
uint sampleLength = sizeof(float) * 3 * 4 + sizeof(uint);
uint realizationLength = sampleLength + sizeof(float) * 2;
_restirRealizations[0] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, (int)realizationLength);
_restirRealizations[1] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, (int)realizationLength);
}
else if (estimationMethod == SurfaceCacheEstimationMethod.Ris)
{
uint luminanceLength = sizeof(float) * 9;
_risAccumulatedLuminances = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, (int)luminanceLength);
}
}
public void Dispose()
{
foreach (var b in _irradiances)
{
b.Dispose();
}
_counterSets.Dispose();
_geometries.Dispose();
_cellIndices.Dispose();
_statistics.Dispose();
_risAccumulatedLuminances?.Dispose();
if (_restirRealizations != null)
{
foreach (var b in _restirRealizations)
b.Dispose();
}
}
}
internal class SurfaceCacheGrid : IDisposable
{
public const int InvalidOffset = Int32.MaxValue;
public const uint InvalidPatchIndex = UInt32.MaxValue; // Must match HLSL side.
public readonly uint GridSize;
public readonly uint CascadeCount;
public readonly float VoxelMinSize;
public Vector3 TargetPos = Vector3.zero;
public int3[] CascadeOffsets;
public GraphicsBuffer CascadeOffsetBuffer;
public GraphicsBuffer CellAllocationMarks;
public GraphicsBuffer CellPatchIndices;
internal SurfaceCacheGrid(uint gridSize, uint cascadeCount, float voxelMinSize)
{
GridSize = gridSize;
CascadeCount = cascadeCount;
VoxelMinSize = voxelMinSize;
CascadeOffsets = new int3[cascadeCount];
for (int i = 0; i < cascadeCount; ++i)
{
CascadeOffsets[i] = new int3(InvalidOffset, InvalidOffset, InvalidOffset);
}
CascadeOffsetBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)cascadeCount, sizeof(int) * 3);
const uint angularResolution = 4; // Must match HLSL side.
uint cellCount = gridSize * gridSize * gridSize * angularResolution * angularResolution * cascadeCount;
var initBuffer = new uint[cellCount];
CellAllocationMarks = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)(cellCount), sizeof(uint));
CellAllocationMarks.SetData(initBuffer);
CellPatchIndices = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)(cellCount), sizeof(uint));
for (int i = 0; i < cellCount; ++i)
initBuffer[i] = InvalidPatchIndex;
CellPatchIndices.SetData(initBuffer);
}
public void Dispose()
{
CascadeOffsetBuffer.Dispose();
CellAllocationMarks.Dispose();
CellPatchIndices.Dispose();
}
}
internal enum SurfaceCacheEstimationMethod
{
Uniform,
Restir,
Ris
}
internal struct SurfaceCacheGridParameterSet
{
public uint GridSize;
public float VoxelMinSize;
public uint CascadeCount;
}
internal struct SurfaceCacheEstimationParameterSet
{
public SurfaceCacheEstimationMethod Method;
public bool MultiBounce;
public uint RestirEstimationConfidenceCap;
public uint RestirEstimationSpatialSampleCount;
public float RestirEstimationSpatialFilterSize;
public uint RestirEstimationValidationFrameInterval;
public uint UniformEstimationSampleCount;
public uint RisEstimationCandidateCount;
public float RisEstimationTargetFunctionUpdateWeight;
}
internal struct SurfaceCachePatchFilteringParameterSet
{
public float TemporalSmoothing;
public bool SpatialFilterEnabled;
public uint SpatialFilterSampleCount;
public float SpatialFilterRadius;
public bool TemporalPostFilterEnabled;
}
internal class SurfaceCacheResourceSet
{
internal ComputeShader ScrollingShader;
internal int ScrollingKernel;
internal uint3 ScrollingKernelGroupSize;
internal ComputeShader EvictionShader;
internal int EvictionKernel;
internal uint3 EvictionKernelGroupSize;
internal ComputeShader DefragShader;
internal int DefragKernel;
internal uint3 DefragKernelGroupSize;
internal LocalKeyword DefragKeyword;
internal IRayTracingShader UniformEstimationShader;
internal IRayTracingShader RestirCandidateTemporalShader;
internal IRayTracingShader RisEstimationShader;
internal ComputeShader RestirSpatialShader;
internal int RestirSpatialKernel;
internal uint3 RestirSpatialKernelGroupSize;
internal ComputeShader RestirEstimationShader;
internal int RestirEstimationKernel;
internal uint3 RestirEstimationKernelGroupSize;
internal ComputeShader SpatialFilteringShader;
internal int SpatialFilteringKernel;
internal uint3 SpatialFilteringKernelGroupSize;
internal ComputeShader TemporalFilteringShader;
internal int TemporalFilteringKernel;
internal uint3 TemporalFilteringKernelGroupSize;
internal readonly uint SubGroupSize;
internal SurfaceCacheResourceSet(uint subGroupSize)
{
SubGroupSize = subGroupSize;
}
internal bool LoadFromRenderPipelineResources(RayTracingContext rtContext)
{
var rpResources = GraphicsSettings.GetRenderPipelineSettings<Rendering.SurfaceCacheRenderPipelineResourceSet>();
if (rpResources == null)
return false;
ScrollingShader = rpResources.scrollingShader;
ScrollingKernel = ScrollingShader.FindKernel("Scroll");
ScrollingShader.GetKernelThreadGroupSizes(ScrollingKernel, out ScrollingKernelGroupSize.x, out ScrollingKernelGroupSize.y, out ScrollingKernelGroupSize.z);
EvictionShader = rpResources.evictionShader;
EvictionKernel = EvictionShader.FindKernel("Evict");
EvictionShader.GetKernelThreadGroupSizes(EvictionKernel, out EvictionKernelGroupSize.x, out EvictionKernelGroupSize.y, out EvictionKernelGroupSize.z);
RestirSpatialShader = rpResources.restirSpatialShader;
RestirSpatialKernel = RestirSpatialShader.FindKernel("ResampleSpatially");
RestirSpatialShader.GetKernelThreadGroupSizes(RestirSpatialKernel, out RestirSpatialKernelGroupSize.x, out RestirSpatialKernelGroupSize.y, out RestirSpatialKernelGroupSize.z);
RestirEstimationShader = rpResources.restirEstimationShader;
RestirEstimationKernel = RestirEstimationShader.FindKernel("Estimate");
RestirEstimationShader.GetKernelThreadGroupSizes(RestirEstimationKernel, out RestirEstimationKernelGroupSize.x, out RestirEstimationKernelGroupSize.y, out RestirEstimationKernelGroupSize.z);
SpatialFilteringShader = rpResources.spatialFilteringShader;
SpatialFilteringKernel = SpatialFilteringShader.FindKernel("FilterSpatially");
SpatialFilteringShader.GetKernelThreadGroupSizes(SpatialFilteringKernel, out SpatialFilteringKernelGroupSize.x, out SpatialFilteringKernelGroupSize.y, out SpatialFilteringKernelGroupSize.z);
TemporalFilteringShader = rpResources.temporalFilteringShader;
TemporalFilteringKernel = TemporalFilteringShader.FindKernel("FilterTemporally");
TemporalFilteringShader.GetKernelThreadGroupSizes(TemporalFilteringKernel, out TemporalFilteringKernelGroupSize.x, out TemporalFilteringKernelGroupSize.y, out TemporalFilteringKernelGroupSize.z);
Debug.Assert(SubGroupSize == 8 || SubGroupSize == 16 || SubGroupSize == 32 || SubGroupSize == 48 || SubGroupSize == 64);
DefragShader = rpResources.defragShader;
var defragKeyword = "SUB_GROUP_SIZE_" + SubGroupSize;
DefragShader.EnableKeyword(defragKeyword);
DefragKernel = DefragShader.FindKernel("Defrag");
DefragShader.GetKernelThreadGroupSizes(DefragKernel, out DefragKernelGroupSize.x, out DefragKernelGroupSize.y, out DefragKernelGroupSize.z);
DefragKeyword = new LocalKeyword(DefragShader, defragKeyword);
DefragShader.DisableKeyword(defragKeyword);
Object uniformEstimationUnifiedObj;
Object restirCandidateTemporalUnifiedObj;
Object risEstimationUnifiedObj;
if (rtContext.BackendType == RayTracingBackend.Compute)
{
uniformEstimationUnifiedObj = rpResources.uniformEstimationComputeShader;
restirCandidateTemporalUnifiedObj = rpResources.restirCandidateTemporalComputeShader;
risEstimationUnifiedObj = rpResources.risEstimationComputeShader;
}
else
{
uniformEstimationUnifiedObj = rpResources.uniformEstimationRayTracingShader;
restirCandidateTemporalUnifiedObj = rpResources.restirCandidateTemporalRayTracingShader;
risEstimationUnifiedObj = rpResources.risEstimationRayTracingShader;
}
UniformEstimationShader = rtContext.CreateRayTracingShader(uniformEstimationUnifiedObj);
RestirCandidateTemporalShader = rtContext.CreateRayTracingShader(restirCandidateTemporalUnifiedObj);
RisEstimationShader = rtContext.CreateRayTracingShader(risEstimationUnifiedObj);
return true;
}
}
internal class SurfaceCache : IDisposable
{
public const uint CascadeMax = 8;
private readonly SurfaceCachePatchList _patchList;
private readonly SurfaceCacheGrid _grid;
private readonly SurfaceCacheRingConfig _ringConfig;
private readonly SurfaceCacheResourceSet _resources;
private GraphicsBuffer _traceScratch;
private SurfaceCacheEstimationParameterSet _estimationParams;
private SurfaceCachePatchFilteringParameterSet _patchFilteringParams;
private float _shortHysteresis;
readonly private uint _defragCount = 2;
readonly private uint _environmentCubemapResolution = 32;
readonly private float _albedoBoost = 1.0f;
public SurfaceCachePatchList PatchList => _patchList;
public SurfaceCacheGrid Grid => _grid;
public SurfaceCacheRingConfig RingConfig => _ringConfig;
private class ScrollingPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal GraphicsBuffer CellAllocationMarks;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer PatchCellIndices;
internal GraphicsBuffer NewCascadeOffsetsDevice;
internal int3[] NewCascadeOffsetsHost;
internal int3[] OldCascadeOffsetsHost;
internal uint GridSize;
internal uint CascadeCount;
}
private class EvictionPassData
{
internal uint PatchCapacity;
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchCounterSets;
internal GraphicsBuffer PatchCellIndices;
internal GraphicsBuffer CellAllocationMarks;
internal GraphicsBuffer CellPatchIndices;
internal uint RingConfigOffset;
internal uint FrameIdx;
}
private class DefragPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal LocalKeyword Keyword;
internal uint PatchCapacity;
internal uint3 ThreadGroupSize;
internal uint IterationOffset;
internal uint IterationCount;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchCellIndices;
internal GraphicsBuffer PatchCounterSets;
internal GraphicsBuffer PatchIrradiances0;
internal GraphicsBuffer PatchIrradiances1;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer CellPatchIndices;
internal uint RingConfigStartFlipflop;
internal uint EvenIterationPatchOffset;
internal uint OddIterationPatchOffset;
}
private class UniformEstimationPassData
{
internal uint PatchCapacity;
internal IRayTracingShader Shader;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer PatchCounterSets;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer CascadeOffsets;
internal SurfaceCacheWorld World;
internal float AlbedoBoost;
internal uint FrameIdx;
internal uint GridSize;
internal uint CascadeCount;
internal bool MultiBounce;
internal float ShortHysteresis;
internal uint RingConfigOffset;
internal uint SampleCount;
internal float VoxelMinSize;
internal Vector3 GridTargetPos;
internal GraphicsBuffer TraceScratchBuffer;
internal uint EnvironmentCubemapResolution;
}
private class RestirCandidateTemporalPassData
{
internal uint PatchCapacity;
internal IRayTracingShader Shader;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchRealizations;
internal GraphicsBuffer CascadeOffsets;
internal GraphicsBuffer CellPatchIndices;
internal Vector3 GridTargetPos;
internal SurfaceCacheWorld World;
internal float AlbedoBoost;
internal uint FrameIdx;
internal uint GridSize;
internal uint RingConfigOffset;
internal uint CascadeCount;
internal bool MultiBounce;
internal uint ConfidenceCap;
internal float VoxelMinSize;
internal uint ValidationFrameInterval;
internal GraphicsBuffer TraceScratchBuffer;
internal uint EnvironmentCubemapResolution;
}
private class RestirSpatialPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint PatchCapacity;
internal uint FrameIdx;
internal uint GridSize;
internal uint CascadeCount;
internal uint RingConfigOffset;
internal float VoxelMinSize;
internal uint SampleCount;
internal float FilterSize;
internal Vector3 GridTargetPos;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer CascadeOffsets;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer InputPatchRealizations;
internal GraphicsBuffer OutputPatchRealizations;
}
private class RestirEstimationPassData
{
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint PatchCapacity;
internal ComputeShader Shader;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchCounterSets;
internal GraphicsBuffer PatchRealizations;
internal uint RingConfigOffset;
internal float ShortHysteresis;
}
private class RisEstimationPassData
{
internal uint PatchCapacity;
internal IRayTracingShader Shader;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchCounterSets;
internal GraphicsBuffer CascadeOffsets;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer PatchAccumulatedLuminances;
internal SurfaceCacheWorld World;
internal float AlbedoBoost;
internal uint FrameIdx;
internal uint GridSize;
internal uint CascadeCount;
internal bool MultiBounce;
internal uint CandidateCount;
internal uint RingConfigOffset;
internal float ShortHysteresis;
internal Vector3 GridTargetPos;
internal float TargetFunctionUpdateWeight;
internal float VoxelMinSize;
internal GraphicsBuffer TraceScratchBuffer;
internal uint EnvironmentCubemapResolution;
}
private class SpatialFilterPassData
{
internal GraphicsBuffer InputPatchIrradiances;
internal GraphicsBuffer OutputPatchIrradiances;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer CascadeOffsets;
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint PatchCapacity;
internal uint FrameIdx;
internal uint CascadeCount;
internal uint GridSize;
internal float VoxelMinSize;
internal uint SampleCount;
internal float Radius;
internal uint RingConfigOffset;
internal Vector3 GridTargetPos;
}
private class TemporalFilterPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal GraphicsBuffer InputPatchIrradiances;
internal GraphicsBuffer OutputPatchIrradiances;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchCounterSets;
internal uint PatchCapacity;
internal uint RingConfigOffset;
internal float ShortHysteresis;
}
internal static class ShaderIDs
{
public static readonly int _CellAllocationMarks = Shader.PropertyToID("_CellAllocationMarks");
public static readonly int _CellPatchIndices = Shader.PropertyToID("_CellPatchIndices");
public static readonly int _MaterialEntries = Shader.PropertyToID("_MaterialEntries");
public static readonly int _AlbedoTextures = Shader.PropertyToID("_AlbedoTextures");
public static readonly int _AlbedoBoost = Shader.PropertyToID("_AlbedoBoost");
public static readonly int _DirectionalLightDirection = Shader.PropertyToID("_DirectionalLightDirection");
public static readonly int _DirectionalLightIntensity = Shader.PropertyToID("_DirectionalLightIntensity");
public static readonly int _MaterialAtlasTexelSize = Shader.PropertyToID("_MaterialAtlasTexelSize");
public static readonly int _TransmissionTextures = Shader.PropertyToID("_TransmissionTextures");
public static readonly int _EmissionTextures = Shader.PropertyToID("_EmissionTextures");
public static readonly int _GridTargetPos = Shader.PropertyToID("_GridTargetPos");
public static readonly int _EnvironmentCubemap = Shader.PropertyToID("_EnvironmentCubemap");
public static readonly int _NewCascadeOffsets = Shader.PropertyToID("_NewCascadeOffsets");
public static readonly int _OldCascadeOffsets = Shader.PropertyToID("_OldCascadeOffsets");
public static readonly int _GridSize = Shader.PropertyToID("_GridSize");
public static readonly int _CascadeCount = Shader.PropertyToID("_CascadeCount");
public static readonly int _ConfidenceCap = Shader.PropertyToID("_ConfidenceCap");
public static readonly int _CandidateCount = Shader.PropertyToID("_CandidateCount");
public static readonly int _TargetFunctionUpdateWeight = Shader.PropertyToID("_TargetFunctionUpdateWeight");
public static readonly int _SampleCount = Shader.PropertyToID("_SampleCount");
public static readonly int _FilterSize = Shader.PropertyToID("_FilterSize");
public static readonly int _MultiBounce = Shader.PropertyToID("_MultiBounce");
public static readonly int _ValidationFrameInterval = Shader.PropertyToID("_ValidationFrameInterval");
public static readonly int _VoxelMinSize = Shader.PropertyToID("_VoxelMinSize");
public static readonly int _ShortHysteresis = Shader.PropertyToID("_ShortHysteresis");
public static readonly int _PatchCellIndices = Shader.PropertyToID("_PatchCellIndices");
public static readonly int _RingConfigBuffer = Shader.PropertyToID("_RingConfigBuffer");
public static readonly int _Radius = Shader.PropertyToID("_Radius");
public static readonly int _InputPatchIrradiances = Shader.PropertyToID("_InputPatchIrradiances");
public static readonly int _OutputPatchIrradiances = Shader.PropertyToID("_OutputPatchIrradiances");
public static readonly int _PatchIrradiances = Shader.PropertyToID("_PatchIrradiances");
public static readonly int _FrameIdx = Shader.PropertyToID("_FrameIdx");
public static readonly int _PatchCounterSets = Shader.PropertyToID("_PatchCounterSets");
public static readonly int _CascadeOffsets = Shader.PropertyToID("_CascadeOffsets");
public static readonly int _PatchIrradiances0 = Shader.PropertyToID("_PatchIrradiances0");
public static readonly int _PatchIrradiances1 = Shader.PropertyToID("_PatchIrradiances1");
public static readonly int _PatchStatistics = Shader.PropertyToID("_PatchStatistics");
public static readonly int _RingConfigReadOffset = Shader.PropertyToID("_RingConfigReadOffset");
public static readonly int _RingConfigWriteOffset = Shader.PropertyToID("_RingConfigWriteOffset");
public static readonly int _PatchOffset = Shader.PropertyToID("_PatchOffset");
public static readonly int _PatchGeometries = Shader.PropertyToID("_PatchGeometries");
public static readonly int _PatchRealizations = Shader.PropertyToID("_PatchRealizations");
public static readonly int _PatchAccumulatedLuminances = Shader.PropertyToID("_PatchAccumulatedLuminances");
public static readonly int _InputPatchRealizations = Shader.PropertyToID("_InputPatchRealizations");
public static readonly int _OutputPatchRealizations = Shader.PropertyToID("_OutputPatchRealizations");
public static readonly int _RingConfigOffset = Shader.PropertyToID("_RingConfigOffset");
}
public SurfaceCache(
SurfaceCacheResourceSet resources,
SurfaceCacheGridParameterSet gridParams,
SurfaceCacheEstimationParameterSet estimationParams,
SurfaceCachePatchFilteringParameterSet patchFilteringParams)
{
Debug.Assert(gridParams.CascadeCount != 0);
Debug.Assert(gridParams.CascadeCount <= CascadeMax);
Debug.Assert(0.0f <= patchFilteringParams.TemporalSmoothing);
Debug.Assert(patchFilteringParams.TemporalSmoothing <= 1.0f);
uint patchCapacity = 65536; // Must match HLSL side constant.
Debug.Assert((UInt64)4294967296 % (UInt64)patchCapacity == 0, "Patch Capacity must be a divisor of 2^32."); // This property is required by the HLSL side ring buffer allocation logic.
_resources = resources;
_grid = new SurfaceCacheGrid(gridParams.GridSize, gridParams.CascadeCount, gridParams.VoxelMinSize);
_ringConfig = new SurfaceCacheRingConfig();
_patchList = new SurfaceCachePatchList(patchCapacity, estimationParams.Method);
_estimationParams = estimationParams;
_patchFilteringParams = patchFilteringParams;
Debug.Assert(0.0f <= patchFilteringParams.TemporalSmoothing && patchFilteringParams.TemporalSmoothing <= 1.0f);
_shortHysteresis = Mathf.Lerp(0.75f, 0.95f, patchFilteringParams.TemporalSmoothing);
}
public void RecordPreparation(RenderGraph renderGraph, uint frameIdx)
{
RecordScrolling(renderGraph);
RecordEviction(renderGraph, frameIdx);
RecordDefragmentation(renderGraph, frameIdx);
}
internal uint RecordPatchUpdate(RenderGraph renderGraph, uint frameIdx, SurfaceCacheWorld world)
{
RecordEstimation(renderGraph, frameIdx, world);
return RecordFiltering(renderGraph, frameIdx);
}
private void RecordDefragmentation(RenderGraph renderGraph, uint frameIdx)
{
using (var builder = renderGraph.AddComputePass("Surface Cache Defrag", out DefragPassData passData))
{
passData.IterationOffset = frameIdx * _defragCount;
passData.IterationCount = _defragCount;
passData.Shader = _resources.DefragShader;
passData.Keyword = _resources.DefragKeyword;
passData.KernelIndex = _resources.DefragKernel;
passData.PatchCapacity = PatchList.Capacity;
passData.RingConfigStartFlipflop = RingConfig.FlipFlop;
passData.ThreadGroupSize = _resources.DefragKernelGroupSize;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchCellIndices = PatchList.CellIndices;
passData.PatchCounterSets = PatchList.CounterSets;
passData.PatchIrradiances0 = PatchList.Irradiances[0];
passData.PatchIrradiances1 = PatchList.Irradiances[2];
passData.PatchGeometries = PatchList.Geometries;
passData.PatchStatistics = PatchList.Statistics;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.EvenIterationPatchOffset = 0;
passData.OddIterationPatchOffset = _resources.SubGroupSize / 2;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((DefragPassData data, ComputeGraphContext cgContext) => Defrag(data, cgContext));
if (_defragCount % 2 == 1)
RingConfig.Flip();
}
}
private void RecordEviction(RenderGraph renderGraph, uint frameIdx)
{
using (var builder = renderGraph.AddComputePass("Surface Cache Eviction", out EvictionPassData passData))
{
passData.Shader = _resources.EvictionShader;
passData.KernelIndex = _resources.EvictionKernel;
passData.ThreadGroupSize = _resources.EvictionKernelGroupSize;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.PatchCounterSets = PatchList.CounterSets;
passData.PatchCellIndices = PatchList.CellIndices;
passData.CellAllocationMarks = Grid.CellAllocationMarks;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.PatchCapacity = PatchList.Capacity;
passData.FrameIdx = frameIdx;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((EvictionPassData data, ComputeGraphContext cgContext) => Evict(data, cgContext));
}
}
private uint RecordFiltering(RenderGraph renderGraph, uint frameIdx)
{
uint outputIrradianceBufferIdx = 0;
if (_patchFilteringParams.SpatialFilterEnabled)
{
outputIrradianceBufferIdx = 1;
using (var builder = renderGraph.AddComputePass("Surface Cache Spatial Filter", out SpatialFilterPassData passData))
{
passData.InputPatchIrradiances = PatchList.Irradiances[0];
passData.OutputPatchIrradiances = PatchList.Irradiances[1];
passData.PatchGeometries = PatchList.Geometries;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.Shader = _resources.SpatialFilteringShader;
passData.KernelIndex = _resources.SpatialFilteringKernel;
passData.ThreadGroupSize = _resources.SpatialFilteringKernelGroupSize;
passData.PatchCapacity = PatchList.Capacity;
passData.FrameIdx = frameIdx;
passData.CascadeCount = Grid.CascadeCount;
passData.GridSize = Grid.GridSize;
passData.VoxelMinSize = Grid.VoxelMinSize;
passData.SampleCount = _patchFilteringParams.SpatialFilterSampleCount;
passData.Radius = _patchFilteringParams.SpatialFilterRadius;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.CascadeOffsets = Grid.CascadeOffsetBuffer;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.GridTargetPos = Grid.TargetPos;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((SpatialFilterPassData data, ComputeGraphContext cgContext) => FilterSpatially(data, cgContext));
}
}
if (_patchFilteringParams.TemporalPostFilterEnabled)
{
using (var builder = renderGraph.AddComputePass("Surface Cache Temporal Filter", out TemporalFilterPassData passData))
{
passData.Shader = _resources.TemporalFilteringShader;
passData.KernelIndex = _resources.TemporalFilteringKernel;
passData.ThreadGroupSize = _resources.TemporalFilteringKernelGroupSize;
passData.InputPatchIrradiances = PatchList.Irradiances[outputIrradianceBufferIdx];
passData.OutputPatchIrradiances = PatchList.Irradiances[2];
passData.PatchStatistics = PatchList.Statistics;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchCounterSets = PatchList.CounterSets;
passData.PatchCapacity = PatchList.Capacity;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.ShortHysteresis = _shortHysteresis;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((TemporalFilterPassData data, ComputeGraphContext cgContext) => FilterTemporally(data, cgContext));
}
outputIrradianceBufferIdx = 2;
}
return outputIrradianceBufferIdx;
}
private void RecordEstimation(RenderGraph renderGraph, uint frameIdx, SurfaceCacheWorld world)
{
if (_estimationParams.Method == SurfaceCacheEstimationMethod.Uniform)
{
using (var builder = renderGraph.AddUnsafePass("Surface Cache Uniform Estimation", out UniformEstimationPassData passData))
{
passData.PatchCapacity = PatchList.Capacity;
passData.Shader = _resources.UniformEstimationShader;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchIrradiances = PatchList.Irradiances[0];
passData.PatchGeometries = PatchList.Geometries;
passData.PatchStatistics = PatchList.Statistics;
passData.PatchCounterSets = PatchList.CounterSets;
passData.World = world;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.GridTargetPos = Grid.TargetPos;
passData.FrameIdx = frameIdx;
passData.AlbedoBoost = _albedoBoost;
passData.GridSize = Grid.GridSize;
passData.CascadeOffsets = Grid.CascadeOffsetBuffer;
passData.CascadeCount = Grid.CascadeCount;
passData.MultiBounce = _estimationParams.MultiBounce;
passData.ShortHysteresis = _shortHysteresis;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.SampleCount = _estimationParams.UniformEstimationSampleCount;
passData.VoxelMinSize = Grid.VoxelMinSize;
RayTracingHelper.ResizeScratchBufferForTrace(passData.Shader, passData.PatchCapacity, 1, 1, ref _traceScratch);
passData.TraceScratchBuffer = _traceScratch;
passData.EnvironmentCubemapResolution = _environmentCubemapResolution;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((UniformEstimationPassData data, UnsafeGraphContext cgContext) => UniformEstimate(data, cgContext));
}
}
else if (_estimationParams.Method == SurfaceCacheEstimationMethod.Restir)
{
using (var builder = renderGraph.AddUnsafePass("Surface Cache Restir Candidate + Temporal", out RestirCandidateTemporalPassData passData))
{
passData.PatchCapacity = PatchList.Capacity;
passData.Shader = _resources.RestirCandidateTemporalShader;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchIrradiances = PatchList.Irradiances[0];
passData.PatchGeometries = PatchList.Geometries;
passData.PatchRealizations = PatchList.RestirRealizations[0];
passData.CascadeOffsets = Grid.CascadeOffsetBuffer;
passData.World = world;
passData.AlbedoBoost = _albedoBoost;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.GridTargetPos = Grid.TargetPos;
passData.FrameIdx = frameIdx;
passData.GridSize = Grid.GridSize;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.CascadeCount = Grid.CascadeCount;
passData.MultiBounce = _estimationParams.MultiBounce;
passData.ConfidenceCap = _estimationParams.RestirEstimationConfidenceCap;
passData.VoxelMinSize = Grid.VoxelMinSize;
passData.ValidationFrameInterval = _estimationParams.RestirEstimationValidationFrameInterval;
passData.EnvironmentCubemapResolution = _environmentCubemapResolution;
RayTracingHelper.ResizeScratchBufferForTrace(passData.Shader, passData.PatchCapacity, 1, 1, ref _traceScratch);
passData.TraceScratchBuffer = _traceScratch;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((RestirCandidateTemporalPassData data, UnsafeGraphContext cgContext) => RestirGenerateCandidateAndResampleTemporally(data, cgContext));
}
using (var builder = renderGraph.AddComputePass("Surface Cache Restir Spatial", out RestirSpatialPassData passData))
{
passData.Shader = _resources.RestirSpatialShader;
passData.KernelIndex = _resources.RestirSpatialKernel;
passData.ThreadGroupSize = _resources.RestirSpatialKernelGroupSize;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.PatchGeometries = PatchList.Geometries;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.CascadeOffsets = Grid.CascadeOffsetBuffer;
passData.InputPatchRealizations = PatchList.RestirRealizations[0];
passData.OutputPatchRealizations = PatchList.RestirRealizations[1];
passData.PatchCapacity = PatchList.Capacity;
passData.FrameIdx = frameIdx;
passData.VoxelMinSize = Grid.VoxelMinSize;
passData.GridSize = Grid.GridSize;
passData.CascadeCount = Grid.CascadeCount;
passData.SampleCount = _estimationParams.RestirEstimationSpatialSampleCount;
passData.FilterSize = _estimationParams.RestirEstimationSpatialFilterSize;
passData.GridTargetPos = Grid.TargetPos;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((RestirSpatialPassData data, ComputeGraphContext cgContext) => RestirResampleSpatially(data, cgContext));
}
using (var builder = renderGraph.AddComputePass("Surface Cache Restir Estimation", out RestirEstimationPassData passData))
{
passData.KernelIndex = _resources.RestirEstimationKernel;
passData.ThreadGroupSize = _resources.RestirEstimationKernelGroupSize;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchGeometries = PatchList.Geometries;
passData.PatchRealizations = PatchList.RestirRealizations[1];
passData.PatchCounterSets = PatchList.CounterSets;
passData.PatchIrradiances = PatchList.Irradiances[0];
passData.PatchStatistics = PatchList.Statistics;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.ShortHysteresis = _shortHysteresis;
passData.Shader = _resources.RestirEstimationShader;
passData.PatchCapacity = PatchList.Capacity;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((RestirEstimationPassData data, ComputeGraphContext cgContext) => RestirEstimate(data, cgContext));
}
}
else if (_estimationParams.Method == SurfaceCacheEstimationMethod.Ris)
{
using (var builder = renderGraph.AddUnsafePass("Surface Cache RIS Estimation", out RisEstimationPassData passData))
{
passData.PatchCapacity = PatchList.Capacity;
passData.Shader = _resources.RisEstimationShader;
passData.RingConfigBuffer = RingConfig.Buffer;
passData.PatchIrradiances = PatchList.Irradiances[0];
passData.PatchStatistics = PatchList.Statistics;
passData.PatchGeometries = PatchList.Geometries;
passData.PatchCounterSets = PatchList.CounterSets;
passData.CascadeOffsets = Grid.CascadeOffsetBuffer;
passData.World = world;
passData.AlbedoBoost = _albedoBoost;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.FrameIdx = frameIdx;
passData.GridSize = Grid.GridSize;
passData.CascadeCount = Grid.CascadeCount;
passData.MultiBounce = _estimationParams.MultiBounce;
passData.CandidateCount = _estimationParams.RisEstimationCandidateCount;
passData.RingConfigOffset = RingConfig.OffsetA;
passData.ShortHysteresis = _shortHysteresis;
passData.GridTargetPos = Grid.TargetPos;
passData.TargetFunctionUpdateWeight = _estimationParams.RisEstimationTargetFunctionUpdateWeight;
passData.VoxelMinSize = Grid.VoxelMinSize;
passData.PatchAccumulatedLuminances = PatchList.RisAccumulatedLuminances;
passData.EnvironmentCubemapResolution = _environmentCubemapResolution;
RayTracingHelper.ResizeScratchBufferForTrace(passData.Shader, passData.PatchCapacity, 1, 1, ref _traceScratch);
passData.TraceScratchBuffer = _traceScratch;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((RisEstimationPassData data, UnsafeGraphContext cgContext) => RisEstimate(data, cgContext));
}
}
else
{
Debug.Assert(false, "Unexpected estimation method.");
}
}
private void RecordScrolling(RenderGraph renderGraph)
{
bool cascadesChanged = false;
Span<int3> oldCascadeOffsets = stackalloc int3[(int)Grid.CascadeCount];
for (int cascadeIdx = 0; cascadeIdx < Grid.CascadeCount; ++cascadeIdx)
{
var camPosGridSpace = _grid.TargetPos / (Grid.VoxelMinSize * (1 << cascadeIdx));
var newOffset = new int3(
(int)Math.Round(camPosGridSpace.x, MidpointRounding.AwayFromZero),
(int)Math.Round(camPosGridSpace.y, MidpointRounding.AwayFromZero),
(int)Math.Round(camPosGridSpace.z, MidpointRounding.AwayFromZero));
var oldOffset = Grid.CascadeOffsets[cascadeIdx];
oldCascadeOffsets[cascadeIdx] = oldOffset;
cascadesChanged = cascadesChanged || math.any(newOffset != oldOffset);
Grid.CascadeOffsets[cascadeIdx] = newOffset;
}
if (cascadesChanged)
{
using (var builder = renderGraph.AddComputePass("Surface Cache Scrolling", out ScrollingPassData passData))
{
passData.Shader = _resources.ScrollingShader;
passData.KernelIndex = _resources.ScrollingKernel;
passData.ThreadGroupSize = _resources.ScrollingKernelGroupSize;
passData.CellAllocationMarks = Grid.CellAllocationMarks;
passData.CellPatchIndices = Grid.CellPatchIndices;
passData.PatchCellIndices = PatchList.CellIndices;
passData.GridSize = Grid.GridSize;
passData.NewCascadeOffsetsDevice = Grid.CascadeOffsetBuffer;
passData.NewCascadeOffsetsHost = Grid.CascadeOffsets;
passData.OldCascadeOffsetsHost = oldCascadeOffsets.ToArray();
passData.CascadeCount = Grid.CascadeCount;
builder.AllowGlobalStateModification(true); // Set to ensure ordering.
builder.SetRenderFunc((ScrollingPassData data, ComputeGraphContext cgContext) => Scroll(data, cgContext));
}
}
}
static void UniformEstimate(UniformEstimationPassData data, UnsafeGraphContext graphCtx)
{
var shader = data.Shader;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd);
shader.SetBufferParam(cmd, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
shader.SetBufferParam(cmd, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
shader.SetBufferParam(cmd, ShaderIDs._PatchGeometries, data.PatchGeometries);
shader.SetBufferParam(cmd, ShaderIDs._PatchStatistics, data.PatchStatistics);
shader.SetBufferParam(cmd, ShaderIDs._PatchCounterSets, data.PatchCounterSets);
shader.SetBufferParam(cmd, ShaderIDs._CascadeOffsets, data.CascadeOffsets);
shader.SetIntParam(cmd, ShaderIDs._FrameIdx, (int)data.FrameIdx);
shader.SetIntParam(cmd, ShaderIDs._GridSize, (int)data.GridSize);
shader.SetIntParam(cmd, ShaderIDs._CascadeCount, (int)data.CascadeCount);
shader.SetIntParam(cmd, ShaderIDs._SampleCount, (int)data.SampleCount);
shader.SetIntParam(cmd, ShaderIDs._MultiBounce, data.MultiBounce ? 1 : 0);
shader.SetFloatParam(cmd, ShaderIDs._VoxelMinSize, data.VoxelMinSize);
shader.SetFloatParam(cmd, ShaderIDs._ShortHysteresis, data.ShortHysteresis);
shader.SetIntParam(cmd, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
shader.SetBufferParam(cmd, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
shader.SetVectorParam(cmd, ShaderIDs._GridTargetPos, data.GridTargetPos);
shader.SetTextureParam(cmd, ShaderIDs._EnvironmentCubemap, data.World.GetEnvironmentTexture((int)data.EnvironmentCubemapResolution));
shader.SetBufferParam(cmd, ShaderIDs._MaterialEntries, data.World.GetMaterialListBuffer());
shader.SetTextureParam(cmd, ShaderIDs._AlbedoTextures, data.World.GetMaterialAlbedoTextures());
shader.SetTextureParam(cmd, ShaderIDs._EmissionTextures, data.World.GetMaterialEmissionTextures());
shader.SetTextureParam(cmd, ShaderIDs._TransmissionTextures, data.World.GetMaterialTransmissionTextures());
shader.SetFloatParam(cmd, ShaderIDs._AlbedoBoost, data.AlbedoBoost);
shader.SetFloatParam(cmd, ShaderIDs._MaterialAtlasTexelSize, GetMaterialAtlasTexelSize(data.World.GetMaterialAlbedoTextures()));
var (dirLightDirection, dirLightIntensity) = GetDirectionalLightUniforms(data.World.GetDirectionalLight());
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightDirection, dirLightDirection);
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightIntensity, dirLightIntensity);
data.World.GetAccelerationStructure().Bind(cmd, "_RayTracingAccelerationStructure", data.Shader);
shader.Dispatch(cmd, data.TraceScratchBuffer, data.PatchCapacity, 1, 1);
}
static (Vector3, Vector3) GetDirectionalLightUniforms(SurfaceCacheWorld.DirectionalLight? dirLight)
{
if (dirLight.HasValue)
return (dirLight.Value.Direction, dirLight.Value.Intensity);
else
return (Vector3.zero, Vector3.zero);
}
static void RestirGenerateCandidateAndResampleTemporally(RestirCandidateTemporalPassData data, UnsafeGraphContext graphCtx)
{
var shader = data.Shader;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd);
shader.SetBufferParam(cmd, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
shader.SetBufferParam(cmd, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
shader.SetBufferParam(cmd, ShaderIDs._PatchGeometries, data.PatchGeometries);
shader.SetBufferParam(cmd, ShaderIDs._PatchRealizations, data.PatchRealizations);
shader.SetBufferParam(cmd, ShaderIDs._CascadeOffsets, data.CascadeOffsets);
shader.SetIntParam(cmd, ShaderIDs._FrameIdx, (int)data.FrameIdx);
shader.SetIntParam(cmd, ShaderIDs._GridSize, (int)data.GridSize);
shader.SetIntParam(cmd, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
shader.SetIntParam(cmd, ShaderIDs._CascadeCount, (int)data.CascadeCount);
shader.SetFloatParam(cmd, ShaderIDs._ConfidenceCap, (float)data.ConfidenceCap);
shader.SetIntParam(cmd, ShaderIDs._MultiBounce, data.MultiBounce ? 1 : 0);
shader.SetIntParam(cmd, ShaderIDs._ValidationFrameInterval, (int)data.ValidationFrameInterval);
shader.SetFloatParam(cmd, ShaderIDs._VoxelMinSize, data.VoxelMinSize);
shader.SetBufferParam(cmd, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
shader.SetVectorParam(cmd, ShaderIDs._GridTargetPos, data.GridTargetPos);
shader.SetTextureParam(cmd, ShaderIDs._EnvironmentCubemap, data.World.GetEnvironmentTexture((int)data.EnvironmentCubemapResolution));
shader.SetBufferParam(cmd, ShaderIDs._MaterialEntries, data.World.GetMaterialListBuffer());
shader.SetTextureParam(cmd, ShaderIDs._AlbedoTextures, data.World.GetMaterialAlbedoTextures());
shader.SetTextureParam(cmd, ShaderIDs._EmissionTextures, data.World.GetMaterialEmissionTextures());
shader.SetTextureParam(cmd, ShaderIDs._TransmissionTextures, data.World.GetMaterialTransmissionTextures());
shader.SetFloatParam(cmd, ShaderIDs._AlbedoBoost, data.AlbedoBoost);
shader.SetFloatParam(cmd, ShaderIDs._MaterialAtlasTexelSize, GetMaterialAtlasTexelSize(data.World.GetMaterialAlbedoTextures()));
var (dirLightIntensity, dirLightDirection) = GetDirectionalLightUniforms(data.World.GetDirectionalLight());
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightIntensity, dirLightIntensity);
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightDirection, dirLightDirection);
data.World.GetAccelerationStructure().Bind(cmd, "_RayTracingAccelerationStructure", data.Shader);
shader.Dispatch(cmd, data.TraceScratchBuffer, data.PatchCapacity, 1, 1);
}
static void RestirResampleSpatially(RestirSpatialPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._InputPatchRealizations, data.InputPatchRealizations);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._OutputPatchRealizations, data.OutputPatchRealizations);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CascadeOffsets, data.CascadeOffsets);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIdx);
cmd.SetComputeFloatParam(shader, ShaderIDs._VoxelMinSize, data.VoxelMinSize);
cmd.SetComputeIntParam(shader, ShaderIDs._GridSize, (int)data.GridSize);
cmd.SetComputeIntParam(shader, ShaderIDs._CascadeCount, (int)data.CascadeCount);
cmd.SetComputeIntParam(shader, ShaderIDs._SampleCount, (int)data.SampleCount);
cmd.SetComputeIntParam(shader, ShaderIDs._FilterSize, (int)data.FilterSize);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
cmd.SetComputeVectorParam(shader, ShaderIDs._GridTargetPos, data.GridTargetPos);
uint3 groupCount = DivUp(new uint3(data.PatchCapacity, 1, 1), data.ThreadGroupSize);
cmd.DispatchCompute(data.Shader, data.KernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void RestirEstimate(RestirEstimationPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchRealizations, data.PatchRealizations);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
cmd.SetComputeFloatParam(shader, ShaderIDs._ShortHysteresis, data.ShortHysteresis);
uint3 groupCount = DivUp(new uint3(data.PatchCapacity, 1, 1), data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void RisEstimate(RisEstimationPassData data, UnsafeGraphContext graphCtx)
{
var shader = data.Shader;
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd);
shader.SetBufferParam(cmd, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
shader.SetBufferParam(cmd, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
shader.SetBufferParam(cmd, ShaderIDs._PatchStatistics, data.PatchStatistics);
shader.SetBufferParam(cmd, ShaderIDs._PatchGeometries, data.PatchGeometries);
shader.SetBufferParam(cmd, ShaderIDs._PatchCounterSets, data.PatchCounterSets);
shader.SetBufferParam(cmd, ShaderIDs._CascadeOffsets, data.CascadeOffsets);
shader.SetIntParam(cmd, ShaderIDs._FrameIdx, (int)data.FrameIdx);
shader.SetIntParam(cmd, ShaderIDs._GridSize, (int)data.GridSize);
shader.SetIntParam(cmd, ShaderIDs._CascadeCount, (int)data.CascadeCount);
shader.SetIntParam(cmd, ShaderIDs._CandidateCount, (int)data.CandidateCount);
shader.SetFloatParam(cmd, ShaderIDs._TargetFunctionUpdateWeight, data.TargetFunctionUpdateWeight);
shader.SetIntParam(cmd, ShaderIDs._MultiBounce, data.MultiBounce ? 1 : 0);
shader.SetFloatParam(cmd, ShaderIDs._VoxelMinSize, data.VoxelMinSize);
shader.SetBufferParam(cmd, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
shader.SetBufferParam(cmd, ShaderIDs._PatchAccumulatedLuminances, data.PatchAccumulatedLuminances);
shader.SetIntParam(cmd, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
shader.SetFloatParam(cmd, ShaderIDs._ShortHysteresis, data.ShortHysteresis);
shader.SetVectorParam(cmd, ShaderIDs._GridTargetPos, data.GridTargetPos);
shader.SetTextureParam(cmd, ShaderIDs._EnvironmentCubemap, data.World.GetEnvironmentTexture((int)data.EnvironmentCubemapResolution));
shader.SetBufferParam(cmd, ShaderIDs._MaterialEntries, data.World.GetMaterialListBuffer());
shader.SetTextureParam(cmd, ShaderIDs._AlbedoTextures, data.World.GetMaterialAlbedoTextures());
shader.SetTextureParam(cmd, ShaderIDs._EmissionTextures, data.World.GetMaterialEmissionTextures());
shader.SetTextureParam(cmd, ShaderIDs._TransmissionTextures, data.World.GetMaterialTransmissionTextures());
shader.SetFloatParam(cmd, ShaderIDs._AlbedoBoost, data.AlbedoBoost);
shader.SetFloatParam(cmd, ShaderIDs._MaterialAtlasTexelSize, GetMaterialAtlasTexelSize(data.World.GetMaterialAlbedoTextures()));
var (dirLightIntensity, dirLightDirection) = GetDirectionalLightUniforms(data.World.GetDirectionalLight());
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightIntensity, dirLightIntensity);
shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightDirection, dirLightDirection);
data.World.GetAccelerationStructure().Bind(cmd, "_RayTracingAccelerationStructure", data.Shader);
shader.Dispatch(cmd, data.TraceScratchBuffer, data.PatchCapacity, 1, 1);
}
static void Defrag(DefragPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.EnableKeyword(shader, data.Keyword);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances0, data.PatchIrradiances0);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances1, data.PatchIrradiances1);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
uint iterationEnd = data.IterationOffset + data.IterationCount;
uint flipflop = data.RingConfigStartFlipflop;
for (uint iterationIndex = data.IterationOffset; iterationIndex < iterationEnd; ++iterationIndex)
{
uint readOffset = SurfaceCacheRingConfig.GetOffsetA(flipflop);
uint writeOffset = SurfaceCacheRingConfig.GetOffsetB(flipflop);
uint patchOffset = iterationIndex % 2 == 0 ? data.EvenIterationPatchOffset : data.OddIterationPatchOffset;
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigReadOffset, (int)readOffset);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigWriteOffset, (int)writeOffset);
cmd.SetComputeIntParam(shader, ShaderIDs._PatchOffset, (int)patchOffset);
uint3 groupCount = DivUp(new uint3(data.PatchCapacity, 1, 1), data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, (int)groupCount.z);
flipflop = SurfaceCacheRingConfig.Flip(flipflop);
}
cmd.DisableKeyword(shader, data.Keyword);
}
static void FilterSpatially(SpatialFilterPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._InputPatchIrradiances, data.InputPatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._OutputPatchIrradiances, data.OutputPatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CascadeOffsets, data.CascadeOffsets);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIdx);
cmd.SetComputeIntParam(shader, ShaderIDs._CascadeCount, (int)data.CascadeCount);
cmd.SetComputeIntParam(shader, ShaderIDs._GridSize, (int)data.GridSize);
cmd.SetComputeFloatParam(shader, ShaderIDs._VoxelMinSize, data.VoxelMinSize);
cmd.SetComputeIntParam(shader, ShaderIDs._SampleCount, (int)data.SampleCount);
cmd.SetComputeFloatParam(shader, ShaderIDs._Radius, data.Radius);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
cmd.SetComputeVectorParam(shader, ShaderIDs._GridTargetPos, data.GridTargetPos);
uint3 groupCount = DivUp(new uint3(data.PatchCapacity, 1, 1), data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void FilterTemporally(TemporalFilterPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._InputPatchIrradiances, data.InputPatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._OutputPatchIrradiances, data.OutputPatchIrradiances);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
cmd.SetComputeFloatParam(shader, ShaderIDs._ShortHysteresis, data.ShortHysteresis);
uint3 groupCount = DivUp(new uint3(data.PatchCapacity, 1, 1), data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
public void Dispose()
{
_grid.Dispose();
_ringConfig.Dispose();
_patchList.Dispose();
_traceScratch?.Dispose();
}
private static uint3 DivUp(uint3 x, uint3 y) => (x + y - 1) / y;
static void Scroll(ScrollingPassData data, ComputeGraphContext cgContext)
{
Debug.Assert(data.NewCascadeOffsetsHost.Length == data.OldCascadeOffsetsHost.Length);
uint cascadeCount = (uint)data.NewCascadeOffsetsHost.Length;
var cmd = cgContext.cmd;
cmd.SetBufferData(data.NewCascadeOffsetsDevice, data.NewCascadeOffsetsHost);
cmd.SetComputeBufferParam(data.Shader, data.KernelIndex, ShaderIDs._CellAllocationMarks, data.CellAllocationMarks);
cmd.SetComputeBufferParam(data.Shader, data.KernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(data.Shader, data.KernelIndex, ShaderIDs._NewCascadeOffsets, data.NewCascadeOffsetsDevice);
cmd.SetComputeBufferParam(data.Shader, data.KernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices);
cmd.SetComputeIntParam(data.Shader, ShaderIDs._GridSize, (int)data.GridSize);
cmd.SetComputeIntParam(data.Shader, ShaderIDs._CascadeCount, (int)data.CascadeCount);
{
var oldCascadeOffsetsInts = new int[data.OldCascadeOffsetsHost.Length * 4];
for (uint cascadeIdx = 0; cascadeIdx < cascadeCount; ++cascadeIdx)
{
oldCascadeOffsetsInts[cascadeIdx * 4] = data.OldCascadeOffsetsHost[cascadeIdx][0];
oldCascadeOffsetsInts[cascadeIdx * 4 + 1] = data.OldCascadeOffsetsHost[cascadeIdx][1];
oldCascadeOffsetsInts[cascadeIdx * 4 + 2] = data.OldCascadeOffsetsHost[cascadeIdx][2];
oldCascadeOffsetsInts[cascadeIdx * 4 + 3] = 0;
}
cmd.SetComputeIntParams(data.Shader, ShaderIDs._OldCascadeOffsets, oldCascadeOffsetsInts);
}
uint3 groupCount = DivUp(new uint3(data.GridSize, data.GridSize, data.GridSize * data.CascadeCount), data.ThreadGroupSize);
cmd.DispatchCompute(data.Shader, data.KernelIndex, (int)groupCount.x, (int)groupCount.y, (int)groupCount.z);
}
static void Evict(EvictionPassData passData, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = passData.Shader;
var kernelIndex = passData.KernelIndex;
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, passData.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, passData.PatchCounterSets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, passData.PatchCellIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellAllocationMarks, passData.CellAllocationMarks);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, passData.CellPatchIndices);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)passData.FrameIdx);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)passData.RingConfigOffset);
uint3 groupCount = DivUp(new uint3(passData.PatchCapacity, 1, 1), passData.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static float GetMaterialAtlasTexelSize(RenderTexture albedoTextures)
{
Debug.Assert(albedoTextures.width == albedoTextures.height, "Atlas textures are assumed to be square.");
return 1.0f / albedoTextures.width;
}
}
}
#endif