Files
T
2026-11-01 23:39:55 +02:00

1159 lines
68 KiB
C#

#if SURFACE_CACHE
using System;
using Unity.Mathematics;
using UnityEngine.Experimental.Rendering;
using UnityEngine.PathTracing.Core;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.UnifiedRayTracing;
namespace UnityEngine.Rendering.Universal
{
internal struct SurfaceCacheScreenFilteringParameterSet
{
public uint LookupSampleCount;
public float UpsamplingKernelSize;
public uint UpsamplingSampleCount;
}
[Serializable]
[SupportedOnRenderPipeline]
[Categorization.CategoryInfo(Name = "R: Surface Cache URP Integration", Order = 1000), HideInInspector]
sealed class SurfaceCacheRenderPipelineResourceSet : IRenderPipelineResources
{
[SerializeField, HideInInspector]
int m_Version = 6;
int IRenderPipelineGraphicsSettings.version => m_Version;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/FallbackMaterial.mat")]
public Material m_FallbackMaterial;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/PatchAllocation.compute")]
public ComputeShader m_AllocationShader;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/ScreenResolveLookup.compute")]
public ComputeShader m_ScreenResolveLookupShader;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/ScreenResolveUpsampling.compute")]
public ComputeShader m_ScreenResolveUpsamplingShader;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/Debug.compute")]
public ComputeShader m_DebugShader;
[ResourcePath("Runtime/RendererFeatures/SurfaceCacheGIRendererFeature/FlatNormalResolution.compute")]
public ComputeShader m_FlatNormalResolutionShader;
public Material fallbackMaterial
{
get => m_FallbackMaterial;
set => this.SetValueAndNotify(ref m_FallbackMaterial, value, nameof(m_FallbackMaterial));
}
public ComputeShader allocationShader
{
get => m_AllocationShader;
set => this.SetValueAndNotify(ref m_AllocationShader, value, nameof(m_AllocationShader));
}
public ComputeShader screenResolveLookupShader
{
get => m_ScreenResolveLookupShader;
set => this.SetValueAndNotify(ref m_ScreenResolveLookupShader, value, nameof(m_ScreenResolveLookupShader));
}
public ComputeShader screenResolveUpsamplingShader
{
get => m_ScreenResolveUpsamplingShader;
set => this.SetValueAndNotify(ref m_ScreenResolveUpsamplingShader, value, nameof(m_ScreenResolveUpsamplingShader));
}
public ComputeShader debugShader
{
get => m_DebugShader;
set => this.SetValueAndNotify(ref m_DebugShader, value, nameof(m_DebugShader));
}
public ComputeShader flatNormalResolutionShader
{
get => m_FlatNormalResolutionShader;
set => this.SetValueAndNotify(ref m_FlatNormalResolutionShader, value, nameof(m_FlatNormalResolutionShader));
}
}
[DisallowMultipleRendererFeature("Surface Cache Global Illumination")]
public class SurfaceCacheGIRendererFeature : ScriptableRendererFeature
{
public enum DebugViewMode_
{
CellIndex,
StableIrradiance,
FastIrradiance,
CoefficientOfVariation,
Drift,
StdDev,
UpdateCount,
FlatNormal
}
public static readonly string k_StaticBatchingErrorMesssage = "Surface Cache GI cannot run because it is incompatible with Static Batching. You can disable the option in the Player Settings.";
private const uint DEFAULT_DEFRAG_COUNT = 2; // Default value for how many patches to defragment per frame.
private const float DEFAULT_VOLUME_SIZE = 128.0f; // Default value for the size of the volume in world units.
private const uint DEFAULT_VOLUME_RESOLUTION = 32; // Default value for the voxel resolution of the volume.
private const uint DEFAULT_VOLUME_CASCADE_COUNT = 4; // Default value for the number of cascades in the volume.
// URP currently cannot render motion vectors properly in Scene View, so we disable it.
// https://jira.unity3d.com/browse/SRP-743
// When this is fixed, we probably want to enable this always.
static bool UseMotionVectorPatchSeeding(CameraType camType)
{
return camType == CameraType.Game;
}
internal static class ShaderIDs
{
public static readonly int _RingConfigBuffer = Shader.PropertyToID("_RingConfigBuffer");
public static readonly int _PatchIrradiances = Shader.PropertyToID("_PatchIrradiances");
public static readonly int _PatchStatistics = Shader.PropertyToID("_PatchStatistics");
public static readonly int _PatchGeometries = Shader.PropertyToID("_PatchGeometries");
public static readonly int _PatchIrradiances0 = Shader.PropertyToID("_PatchIrradiances0");
public static readonly int _PatchIrradiances1 = Shader.PropertyToID("_PatchIrradiances1");
public static readonly int _CellAllocationMarks = Shader.PropertyToID("_CellAllocationMarks");
public static readonly int _CellPatchIndices = Shader.PropertyToID("_CellPatchIndices");
public static readonly int _Result = Shader.PropertyToID("_Result");
public static readonly int _FullResDepths = Shader.PropertyToID("_FullResDepths");
public static readonly int _FullResIrradiances = Shader.PropertyToID("_FullResIrradiances");
public static readonly int _FullResFlatNormals = Shader.PropertyToID("_FullResFlatNormals");
public static readonly int _FullResShadedNormals = Shader.PropertyToID("_FullResShadedNormals");
public static readonly int _UseMotionVectorSeeding = Shader.PropertyToID("_UseMotionVectorSeeding");
public static readonly int _ResultL0 = Shader.PropertyToID("_ResultL0");
public static readonly int _ResultL10 = Shader.PropertyToID("_ResultL10");
public static readonly int _ResultL11 = Shader.PropertyToID("_ResultL11");
public static readonly int _ResultL12 = Shader.PropertyToID("_ResultL12");
public static readonly int _ResultNdcDepths = Shader.PropertyToID("_ResultNdcDepths");
public static readonly int _ScreenDepths = Shader.PropertyToID("_ScreenDepths");
public static readonly int _ScreenFlatNormals = Shader.PropertyToID("_ScreenFlatNormals");
public static readonly int _CurrentFullResScreenDepths = Shader.PropertyToID("_CurrentFullResScreenDepths");
public static readonly int _CurrentFullResScreenFlatNormals = Shader.PropertyToID("_CurrentFullResScreenFlatNormals");
public static readonly int _CurrentFullResScreenMotionVectors = Shader.PropertyToID("_CurrentFullResScreenMotionVectors");
public static readonly int _ScreenShadedNormals = Shader.PropertyToID("_ScreenShadedNormals");
public static readonly int _LowResIrradiancesL0 = Shader.PropertyToID("_LowResIrradiancesL0");
public static readonly int _LowResIrradiancesL10 = Shader.PropertyToID("_LowResIrradiancesL10");
public static readonly int _LowResIrradiancesL11 = Shader.PropertyToID("_LowResIrradiancesL11");
public static readonly int _LowResIrradiancesL12 = Shader.PropertyToID("_LowResIrradiancesL12");
public static readonly int _PreviousLowResScreenIrradiancesL0 = Shader.PropertyToID("_PreviousLowResScreenIrradiancesL0");
public static readonly int _PreviousLowResScreenIrradiancesL10 = Shader.PropertyToID("_PreviousLowResScreenIrradiancesL10");
public static readonly int _PreviousLowResScreenIrradiancesL11 = Shader.PropertyToID("_PreviousLowResScreenIrradiancesL11");
public static readonly int _PreviousLowResScreenIrradiancesL12 = Shader.PropertyToID("_PreviousLowResScreenIrradiancesL12");
public static readonly int _PreviousLowResScreenNdcDepths = Shader.PropertyToID("_PreviousLowResScreenNdcDepths");
public static readonly int _FilterRadius = Shader.PropertyToID("_FilterRadius");
public static readonly int _ViewMode = Shader.PropertyToID("_ViewMode");
public static readonly int _ShowSamplePosition = Shader.PropertyToID("_ShowSamplePosition");
public static readonly int _SampleCount = Shader.PropertyToID("_SampleCount");
public static readonly int _ClipToWorldTransform = Shader.PropertyToID("_ClipToWorldTransform");
public static readonly int _CurrentClipToWorldTransform = Shader.PropertyToID("_CurrentClipToWorldTransform");
public static readonly int _PreviousClipToWorldTransform = Shader.PropertyToID("_PreviousClipToWorldTransform");
public static readonly int _FrameIdx = Shader.PropertyToID("_FrameIdx");
public static readonly int _VolumeSpatialResolution = Shader.PropertyToID("_VolumeSpatialResolution");
public static readonly int _RingConfigOffset = Shader.PropertyToID("_RingConfigOffset");
public static readonly int _VolumeTargetPos = Shader.PropertyToID("_VolumeTargetPos");
public static readonly int _FullResPixelOffset = Shader.PropertyToID("_FullResPixelOffset");
public static readonly int _LowResScreenSize = Shader.PropertyToID("_LowResScreenSize");
public static readonly int _VolumeCascadeCount = Shader.PropertyToID("_VolumeCascadeCount");
public static readonly int _VolumeVoxelMinSize = Shader.PropertyToID("_VolumeVoxelMinSize");
public static readonly int _VolumeCascadeOffsets = Shader.PropertyToID("_VolumeCascadeOffsets");
public static readonly int _PatchCellIndices = Shader.PropertyToID("_PatchCellIndices");
}
class SurfaceCachePass : ScriptableRenderPass, IDisposable
{
private class WorldUpdatePassData
{
internal SurfaceCacheWorld World;
internal uint EnvCubemapResolution;
internal Light Sun;
internal Matrix4x4 RestoreViewMatrix;
internal Matrix4x4 RestoreProjectionMatrix;
}
private class DebugPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint3 ThreadCount;
internal TextureHandle ScreenDepths;
internal TextureHandle ScreenShadedNormals;
internal TextureHandle ScreenFlatNormals;
internal TextureHandle ScreenIrradiances;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchCellIndices;
internal GraphicsBuffer PatchStatistics;
internal DebugViewMode_ ViewMode;
internal uint FrameIndex;
internal bool ShowSamplePosition;
internal uint VolumeSpatialResolution;
internal float VolumeVoxelMinSize;
internal uint VolumeCascadeCount;
internal GraphicsBuffer CascadeOffsets;
internal uint RingConfigOffset;
internal Matrix4x4 ClipToWorldTransform;
internal Vector3 VolumeTargetPos;
}
private class FlatNormalResolutionPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint3 ThreadCount;
internal TextureHandle ScreenDepths;
internal TextureHandle ScreenFlatNormals;
internal Matrix4x4 ClipToWorldTransform;
}
private class PatchAllocationPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint3 ThreadCount;
internal TextureHandle ScreenDepths;
internal TextureHandle ScreenFlatNormals;
internal TextureHandle ScreenMotionVectors;
internal TextureHandle LowResScreenIrradiancesL0;
internal TextureHandle LowResScreenIrradiancesL10;
internal TextureHandle LowResScreenIrradiancesL11;
internal TextureHandle LowResScreenIrradiancesL12;
internal TextureHandle LowResScreenNdcDepths;
internal GraphicsBuffer CellAllocationMarks;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer RingConfigBuffer;
internal GraphicsBuffer PatchIrradiances0;
internal GraphicsBuffer PatchIrradiances1;
internal GraphicsBuffer PatchGeometries;
internal GraphicsBuffer PatchCellIndices;
internal GraphicsBuffer PatchStatistics;
internal uint FrameIdx;
internal uint VolumeSpatialResolution;
internal uint VolumeCascadeCount;
internal uint RingConfigOffset;
internal bool UseMotionVectorSeeding;
internal GraphicsBuffer CascadeOffsets;
internal float VoxelMinSize;
internal Matrix4x4 CurrentClipToWorldTransform;
internal Matrix4x4 PreviousClipToWorldTransform;
internal Vector3 VolumeTargetPos;
internal uint2 FullResPixelOffset;
internal uint2 LowResScreenSize;
}
private class ScreenIrradianceLookupPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint3 ThreadCount;
internal TextureHandle FullResDepths;
internal TextureHandle FullResFlatNormals;
internal TextureHandle LowResScreenIrradiancesL0;
internal TextureHandle LowResScreenIrradiancesL10;
internal TextureHandle LowResScreenIrradiancesL11;
internal TextureHandle LowResScreenIrradiancesL12;
internal TextureHandle LowResScreenNdcDepths;
internal GraphicsBuffer CellPatchIndices;
internal GraphicsBuffer PatchIrradiances;
internal GraphicsBuffer PatchStatistics;
internal GraphicsBuffer CascadeOffsets;
internal uint VolumeSpatialResolution;
internal uint VolumeCascadeCount;
internal uint SampleCount;
internal uint FrameIndex;
internal float VolumeVoxelMinSize;
internal Matrix4x4 ClipToWorldTransform;
internal Vector3 VolumeTargetPos;
}
private class ScreenIrradianceUpsamplingPassData
{
internal ComputeShader Shader;
internal int KernelIndex;
internal uint3 ThreadGroupSize;
internal uint3 FullResThreadCount;
internal TextureHandle FullResDepths;
internal TextureHandle FullResShadedNormals;
internal TextureHandle FullResFlatNormals;
internal TextureHandle LowResScreenIrradiancesL0;
internal TextureHandle LowResScreenIrradiancesL10;
internal TextureHandle LowResScreenIrradiancesL11;
internal TextureHandle LowResScreenIrradiancesL12;
internal TextureHandle FullResIrradiances;
internal float FilterRadius;
internal uint SampleCount;
internal Matrix4x4 ClipToWorldTransform;
}
private RTHandle _fullResScreenIrradiances;
private RTHandle _fullResScreenFlatNormals;
private RTHandle _lowResScreenIrradiancesL0;
private RTHandle _lowResScreenIrradiancesL10;
private RTHandle _lowResScreenIrradiancesL11;
private RTHandle _lowResScreenIrradiancesL12;
private RTHandle _lowResScreenNdcDepths;
private GraphicsBuffer _worldUpdateScratch;
private readonly SurfaceCacheWorldAdapter _worldAdapter;
private readonly InternalBridge.ObjectDispatcher _objDispatcher;
private readonly SurfaceCacheWorld _world;
private readonly ComputeShader _screenResolveLookupShader;
private readonly ComputeShader _screenResolveUpsamplingShader;
private readonly ComputeShader _patchAllocationShader;
private readonly ComputeShader _debugShader;
private readonly ComputeShader _flatNormalResolutionShader;
private readonly RayTracingContext _rtContext;
private readonly int _screenResolveLookupKernel;
private readonly int _screenResolveUpsamplingKernel;
private readonly int _patchAllocationKernel;
private readonly int _debugKernel;
private readonly int _flatNormalResolutionKernel;
private uint3 _screenResolveLookupKernelGroupSize;
private uint3 _screenResolveUpsamplingKernelGroupSize;
private uint3 _patchAllocationKernelGroupSize;
private uint3 _debugKernelGroupSize;
private uint3 _flatNormalResolutionKernelGroupSize;
private uint _frameIdx;
// Debug
private readonly bool _debugEnabled;
private readonly DebugViewMode_ _debugViewMode;
private readonly bool _debugShowSamplePosition;
private SurfaceCache _cache;
private Matrix4x4 _prevClipToWorldTransform = Matrix4x4.identity;
private readonly uint _environmentCubemapResolution = 32;
private const int k_UpscaleFactor = 4;
// Defaults, used as fallback when no volume override is active.
private const uint DEFAULT_ESTIMATION_SAMPLE_COUNT = 2;
private const float DEFAULT_TEMPORAL_SMOOTHING = 0.8f;
private const uint DEFAULT_SPATIAL_FILTER_SAMPLE_COUNT = 4;
private const float DEFAULT_SPATIAL_FILTER_RADIUS = 1.0f;
private const uint DEFAULT_LOOKUP_SAMPLE_COUNT = 6;
private const float DEFAULT_UPSAMPLING_KERNEL_SIZE = 5.0f;
private const uint DEFAULT_UPSAMPLING_SAMPLE_COUNT = 2;
// Stored for runtime cache recreation when resolution or cascade count changes
private readonly Rendering.SurfaceCacheResourceSet _coreResources;
public SurfaceCachePass(
RayTracingContext rtContext,
UnityEngine.Rendering.SurfaceCacheResourceSet resourceSet,
WorldResourceSet worldResources,
ComputeShader patchAllocationShader,
ComputeShader screenResolveLookupShader,
ComputeShader screenResolveUpsamplingShader,
ComputeShader debugShader,
ComputeShader flatNormalResolutionShader,
Material fallbackMaterial,
bool debugEnabled,
DebugViewMode_ debugViewMode,
bool debugShowSamplePosition,
SurfaceCacheVolumeParameterSet volParams)
{
Debug.Assert(volParams.CascadeCount != 0);
Debug.Assert(volParams.CascadeCount <= SurfaceCache.CascadeMax);
_screenResolveLookupShader = screenResolveLookupShader;
_screenResolveUpsamplingShader = screenResolveUpsamplingShader;
_debugShader = debugShader;
_flatNormalResolutionShader = flatNormalResolutionShader;
_patchAllocationShader = patchAllocationShader;
_rtContext = rtContext;
_screenResolveLookupKernel = _screenResolveLookupShader.FindKernel("Lookup");
_screenResolveUpsamplingKernel = _screenResolveUpsamplingShader.FindKernel("Upsample");
_patchAllocationKernel = _patchAllocationShader.FindKernel("Allocate");
_debugKernel = _debugShader.FindKernel("Visualize");
_flatNormalResolutionKernel = _flatNormalResolutionShader.FindKernel("ResolveFlatNormals");
_screenResolveLookupShader.GetKernelThreadGroupSizes(_screenResolveLookupKernel, out _screenResolveLookupKernelGroupSize.x, out _screenResolveLookupKernelGroupSize.y, out _screenResolveLookupKernelGroupSize.z);
_screenResolveUpsamplingShader.GetKernelThreadGroupSizes(_screenResolveUpsamplingKernel, out _screenResolveUpsamplingKernelGroupSize.x, out _screenResolveUpsamplingKernelGroupSize.y, out _screenResolveUpsamplingKernelGroupSize.z);
_patchAllocationShader.GetKernelThreadGroupSizes(_patchAllocationKernel, out _patchAllocationKernelGroupSize.x, out _patchAllocationKernelGroupSize.y, out _patchAllocationKernelGroupSize.z);
_debugShader.GetKernelThreadGroupSizes(_debugKernel, out _debugKernelGroupSize.x, out _debugKernelGroupSize.y, out _debugKernelGroupSize.z);
_flatNormalResolutionShader.GetKernelThreadGroupSizes(_flatNormalResolutionKernel, out _flatNormalResolutionKernelGroupSize.x, out _flatNormalResolutionKernelGroupSize.y, out _flatNormalResolutionKernelGroupSize.z);
_frameIdx = 0;
_debugEnabled = debugEnabled;
_debugViewMode = debugViewMode;
_debugShowSamplePosition = debugShowSamplePosition;
_coreResources = resourceSet;
_cache = new SurfaceCache(resourceSet, volParams);
_cache.SetEmissiveTriangleIntensityMultiplier(k_EmissiveTriangleIntensityMultiplier);
_world = new SurfaceCacheWorld();
_world.Init(_rtContext, worldResources);
_objDispatcher = new();
_worldAdapter = new SurfaceCacheWorldAdapter(_objDispatcher, _world, fallbackMaterial);
}
public void Dispose()
{
_fullResScreenIrradiances?.Release();
_fullResScreenFlatNormals?.Release();
_lowResScreenIrradiancesL0?.Release();
_lowResScreenIrradiancesL10?.Release();
_lowResScreenIrradiancesL11?.Release();
_lowResScreenIrradiancesL12?.Release();
_lowResScreenNdcDepths?.Release();
_cache.Dispose();
_worldAdapter.CleanUp(_world);
_objDispatcher.Dispose();
_world.Dispose();
_worldUpdateScratch?.Dispose();
}
// Historically, GI systems in Unity have, for historical reasons, 1) multiplied punctual light intensity
// inputs with PI, and 2) divided all GI output with PI. To match this, Surface Cache for now does
// something mathematically equivalent: It divides environment light and triangle emission by PI.
// Ideally, we'd get rid of this behavior across all Unity GI systems in the future.
// Note that this is distinct from the separate issue that URP is generally off by PI in its output.
private const bool k_ConformToUnityGIFormat = true;
private const float k_EmissiveTriangleIntensityMultiplier = k_ConformToUnityGIFormat ? 1.0f / Mathf.PI : 1.0f;
private static VolumeParameterSet GetVolumeParametersOrDefaults(SurfaceCacheGIVolumeOverride volume)
{
if (volume != null)
{
// Use volume parameters
return new VolumeParameterSet
{
EstimationParams = new SurfaceCacheEstimationParameterSet
{
MultiBounce = volume.multiBounce.value,
BouncePatchAllocation = volume.bouncePatchAllocation.value,
SampleCount = (uint)volume.sampleCount.value,
},
PatchFilteringParams = new SurfaceCachePatchFilteringParameterSet
{
TemporalSmoothing = volume.temporalSmoothing.value,
SpatialFilterEnabled = volume.spatialFilterEnabled.value,
SpatialFilterSampleCount = (uint)volume.spatialSampleCount.value,
SpatialFilterRadius = volume.spatialRadius.value,
TemporalPostFilterEnabled = volume.temporalPostFilter.value
},
ScreenFilteringParams = new SurfaceCacheScreenFilteringParameterSet
{
LookupSampleCount = (uint)volume.lookupSampleCount.value,
UpsamplingKernelSize = volume.upsamplingKernelSize.value,
UpsamplingSampleCount = (uint)volume.upsamplingSampleCount.value,
},
CascadeMovement = volume.cascadeMovement.value,
VolumeSize = volume.volumeSize.value,
VolumeResolution = (uint)volume.volumeResolution.value,
VolumeCascadeCount = (uint)volume.volumeCascadeCount.value,
DefragCount = (uint)volume.defragCount.value,
RenderingLayerMask = volume.renderingLayerMask.value
};
}
else
{
// Fallback to defaults.
return new VolumeParameterSet
{
EstimationParams = new SurfaceCacheEstimationParameterSet
{
MultiBounce = true,
BouncePatchAllocation = false,
SampleCount = DEFAULT_ESTIMATION_SAMPLE_COUNT,
},
PatchFilteringParams = new SurfaceCachePatchFilteringParameterSet
{
TemporalSmoothing = DEFAULT_TEMPORAL_SMOOTHING,
SpatialFilterEnabled = true,
SpatialFilterSampleCount = DEFAULT_SPATIAL_FILTER_SAMPLE_COUNT,
SpatialFilterRadius = DEFAULT_SPATIAL_FILTER_RADIUS,
TemporalPostFilterEnabled = true
},
ScreenFilteringParams = new SurfaceCacheScreenFilteringParameterSet
{
LookupSampleCount = DEFAULT_LOOKUP_SAMPLE_COUNT,
UpsamplingKernelSize = DEFAULT_UPSAMPLING_KERNEL_SIZE,
UpsamplingSampleCount = DEFAULT_UPSAMPLING_SAMPLE_COUNT,
},
CascadeMovement = true,
VolumeSize = DEFAULT_VOLUME_SIZE,
VolumeResolution = DEFAULT_VOLUME_RESOLUTION,
VolumeCascadeCount = DEFAULT_VOLUME_CASCADE_COUNT,
DefragCount = DEFAULT_DEFRAG_COUNT,
RenderingLayerMask = 0xFFFFFFFF
};
}
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
Debug.Assert(resourceData.cameraDepthTexture.IsValid());
Debug.Assert(resourceData.cameraNormalsTexture.IsValid());
Debug.Assert(resourceData.motionVectorColor.IsValid());
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
var worldToViewTransform = cameraData.GetViewMatrix();
var viewToClipTransform = cameraData.GetGPUProjectionMatrix(true);
var worldToClipTransform = viewToClipTransform * worldToViewTransform;
var clipToWorldTransform = worldToClipTransform.inverse;
var screenResolution = new int2(cameraData.pixelWidth, cameraData.pixelHeight);
// This avoids applying surface cache to e.g. preview cameras.
if (cameraData.cameraType != CameraType.Game && cameraData.cameraType != CameraType.SceneView)
return;
// Get volume parameters that can change per-frame.
var stack = VolumeManager.instance.stack;
var volume = stack.GetComponent<SurfaceCacheGIVolumeOverride>();
var volumeParams = GetVolumeParametersOrDefaults(volume);
// Detect structural changes that require buffer reallocation.
if (volumeParams.VolumeResolution != _cache.Volume.SpatialResolution || volumeParams.VolumeCascadeCount != _cache.Volume.CascadeCount)
{
uint newResolution = volumeParams.VolumeResolution;
uint newCascadeCount = volumeParams.VolumeCascadeCount;
Debug.Assert(newCascadeCount != 0 && newCascadeCount <= SurfaceCache.CascadeMax);
_cache.Dispose();
var newVolParams = new SurfaceCacheVolumeParameterSet
{
Resolution = newResolution,
Size = volumeParams.VolumeSize,
CascadeCount = newCascadeCount
};
_cache = new SurfaceCache(_coreResources, newVolParams);
_cache.SetEmissiveTriangleIntensityMultiplier(k_EmissiveTriangleIntensityMultiplier);
_frameIdx = 0;
}
_cache.SetEstimationParams(volumeParams.EstimationParams);
_cache.SetPatchFilteringParams(volumeParams.PatchFilteringParams);
_cache.SetVolumeSize(volumeParams.VolumeSize);
_cache.SetDefragCount(volumeParams.DefragCount);
bool useMotionVectorPatchSeeding = UseMotionVectorPatchSeeding(cameraData.cameraType);
if (_fullResScreenIrradiances == null || _fullResScreenIrradiances.GetScaledSize() != new Vector2Int(screenResolution.x, screenResolution.y))
{
int lowResWidth = (screenResolution.x + k_UpscaleFactor - 1) / k_UpscaleFactor;
int lowResHeight = (screenResolution.y + k_UpscaleFactor - 1) / k_UpscaleFactor;
_fullResScreenIrradiances?.Release();
_fullResScreenIrradiances = RTHandles.Alloc((int)screenResolution.x, (int)screenResolution.y, 1, DepthBits.None, GraphicsFormat.R16G16B16A16_SFloat, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_fullResScreenIrradiances");
_fullResScreenFlatNormals?.Release();
_fullResScreenFlatNormals = RTHandles.Alloc((int)screenResolution.x, (int)screenResolution.y, 1, DepthBits.None, GraphicsFormat.R8G8B8A8_SNorm, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_fullResScreenFlatNormals");
var l0Format = GraphicsFormat.R16G16B16A16_SFloat;
var l1Format = GraphicsFormat.R8G8B8A8_UNorm;
_lowResScreenIrradiancesL0?.Release();
_lowResScreenIrradiancesL0 = RTHandles.Alloc(lowResWidth, lowResHeight, 1, DepthBits.None, l0Format, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_lowResScreenIrradiancesL0");
_lowResScreenIrradiancesL10?.Release();
_lowResScreenIrradiancesL10 = RTHandles.Alloc(lowResWidth, lowResHeight, 1, DepthBits.None, l1Format, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_lowResScreenIrradiancesL10");
_lowResScreenIrradiancesL11?.Release();
_lowResScreenIrradiancesL11 = RTHandles.Alloc(lowResWidth, lowResHeight, 1, DepthBits.None, l1Format, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_lowResScreenIrradiancesL11");
_lowResScreenIrradiancesL12?.Release();
_lowResScreenIrradiancesL12 = RTHandles.Alloc(lowResWidth, lowResHeight, 1, DepthBits.None, l1Format, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_lowResScreenIrradiancesL12");
_lowResScreenNdcDepths?.Release();
_lowResScreenNdcDepths = RTHandles.Alloc(lowResWidth, lowResHeight, 1, DepthBits.None, GraphicsFormat.R16_UNorm, FilterMode.Point, TextureWrapMode.Clamp, TextureDimension.Tex2D, true, name: "_lowResScreenNdcDepths");
}
if (volumeParams.CascadeMovement || _frameIdx == 0)
{
_cache.Volume.TargetPos = cameraData.camera.transform.position;
}
_cache.RecordPreparation(renderGraph, _frameIdx);
var fullResScreenIrradiancesHandle = renderGraph.ImportTexture(_fullResScreenIrradiances);
var fullResScreenFlatNormalsHandle = renderGraph.ImportTexture(_fullResScreenFlatNormals);
var lowResScreenIrradiancesL0Handle = renderGraph.ImportTexture(_lowResScreenIrradiancesL0);
var lowResScreenIrradiancesL10Handle = renderGraph.ImportTexture(_lowResScreenIrradiancesL10);
var lowResScreenIrradiancesL11Handle = renderGraph.ImportTexture(_lowResScreenIrradiancesL11);
var lowResScreenIrradiancesL12Handle = renderGraph.ImportTexture(_lowResScreenIrradiancesL12);
var lowResScreenNdcDepthsHandle = renderGraph.ImportTexture(_lowResScreenNdcDepths);
var cellAllocationMarkHandle = renderGraph.ImportBuffer(_cache.Volume.CellAllocationMarks);
using (var builder = renderGraph.AddComputePass("Surface Cache Flat Normal Resolution", out FlatNormalResolutionPassData passData))
{
passData.ThreadCount = new uint3((uint)screenResolution.x, (uint)screenResolution.y, 1);
passData.Shader = _flatNormalResolutionShader;
passData.KernelIndex = _flatNormalResolutionKernel;
passData.ThreadGroupSize = _flatNormalResolutionKernelGroupSize;
passData.ScreenDepths = resourceData.cameraDepthTexture;
passData.ScreenFlatNormals = fullResScreenFlatNormalsHandle;
passData.ClipToWorldTransform = clipToWorldTransform;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(fullResScreenFlatNormalsHandle, AccessFlags.Write);
builder.SetRenderFunc((FlatNormalResolutionPassData data, ComputeGraphContext cgContext) => ResolveFlatNormals(data, cgContext));
}
using (var builder = renderGraph.AddComputePass("Surface Cache Patch Allocation", out PatchAllocationPassData passData))
{
uint2 fullResScreenSize = new uint2((uint)screenResolution.x, (uint)screenResolution.y);
uint2 lowResScreenSize = DivUp(fullResScreenSize, new uint2(k_UpscaleFactor, k_UpscaleFactor));
passData.ThreadCount = new uint3(lowResScreenSize, 1);
passData.Shader = _patchAllocationShader;
passData.KernelIndex = _patchAllocationKernel;
passData.ThreadGroupSize = _patchAllocationKernelGroupSize;
passData.ScreenDepths = resourceData.cameraDepthTexture;
passData.ScreenFlatNormals = fullResScreenFlatNormalsHandle;
passData.ScreenMotionVectors = resourceData.motionVectorColor;
passData.LowResScreenIrradiancesL0 = lowResScreenIrradiancesL0Handle;
passData.LowResScreenIrradiancesL10 = lowResScreenIrradiancesL10Handle;
passData.LowResScreenIrradiancesL11 = lowResScreenIrradiancesL11Handle;
passData.LowResScreenIrradiancesL12 = lowResScreenIrradiancesL12Handle;
passData.LowResScreenNdcDepths = lowResScreenNdcDepthsHandle;
passData.CellAllocationMarks = _cache.Volume.CellAllocationMarks;
passData.CellPatchIndices = _cache.Volume.CellPatchIndices;
passData.RingConfigBuffer = _cache.RingConfig.Buffer;
passData.PatchIrradiances0 = _cache.Patches.Irradiances[0];
passData.PatchIrradiances1 = _cache.Patches.Irradiances[2];
passData.PatchGeometries = _cache.Patches.Geometries;
passData.PatchCellIndices = _cache.Patches.CellIndices;
passData.PatchStatistics = _cache.Patches.Statistics;
passData.FrameIdx = _frameIdx;
passData.VolumeSpatialResolution = _cache.Volume.SpatialResolution;
passData.VolumeCascadeCount = _cache.Volume.CascadeCount;
passData.RingConfigOffset = _cache.RingConfig.OffsetA;
{
Debug.Assert(k_UpscaleFactor == 4);
uint cycleIndex = _frameIdx % 4;
uint shuffledCycleIndex = cycleIndex * 7 % 16;
passData.FullResPixelOffset = new uint2(shuffledCycleIndex / 4, shuffledCycleIndex % 4);
}
passData.LowResScreenSize = lowResScreenSize;
passData.UseMotionVectorSeeding = useMotionVectorPatchSeeding;
passData.CascadeOffsets = _cache.Volume.CascadeOffsetBuffer;
passData.VoxelMinSize = _cache.Volume.VoxelMinSize;
passData.CurrentClipToWorldTransform = clipToWorldTransform;
passData.PreviousClipToWorldTransform = _prevClipToWorldTransform;
passData.VolumeTargetPos = _cache.Volume.TargetPos;
builder.UseBuffer(cellAllocationMarkHandle, AccessFlags.Write);
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(fullResScreenFlatNormalsHandle, AccessFlags.Read);
builder.UseTexture(resourceData.motionVectorColor, AccessFlags.Read);
builder.UseTexture(lowResScreenIrradiancesL0Handle, AccessFlags.Read);
builder.UseTexture(lowResScreenIrradiancesL10Handle, AccessFlags.Read);
builder.UseTexture(lowResScreenIrradiancesL11Handle, AccessFlags.Read);
builder.UseTexture(lowResScreenIrradiancesL12Handle, AccessFlags.Read);
builder.UseTexture(lowResScreenNdcDepthsHandle, AccessFlags.Read);
builder.SetRenderFunc((PatchAllocationPassData data, ComputeGraphContext cgContext) => AllocatePatches(data, cgContext));
}
// RenderSettings.ambientIntensity is used directly as a linear value for Surface Cache, which is not currently
// the case for the standard ambient probe lighting, which is assumed to be in gamma space and then converted to
// linear space. We will make this more coherent for the ambient probe in the future.
// Similarly, the ambient colors are all defined in sRGB space and must be converted to linear.
_worldAdapter.Update(
_objDispatcher,
RenderSettings.ambientMode,
RenderSettings.skybox,
RenderSettings.ambientSkyColor.linear,
RenderSettings.ambientEquatorColor.linear,
RenderSettings.ambientGroundColor.linear,
RenderSettings.ambientIntensity,
k_ConformToUnityGIFormat,
volumeParams.RenderingLayerMask,
_world);
using (var builder = renderGraph.AddUnsafePass("Surface Cache World Update", out WorldUpdatePassData passData))
{
passData.World = _world;
passData.EnvCubemapResolution = _environmentCubemapResolution;
passData.Sun = RenderSettings.sun;
passData.RestoreProjectionMatrix = cameraData.GetProjectionMatrix();
passData.RestoreViewMatrix = worldToViewTransform;
builder.AllowGlobalStateModification(true);
builder.SetRenderFunc((WorldUpdatePassData data, UnsafeGraphContext graphCtx) => UpdateWorld(data, graphCtx, ref _worldUpdateScratch));
}
uint outputIrradianceBufferIdx = _cache.RecordPatchUpdate(renderGraph, _frameIdx, _world);
using (var builder = renderGraph.AddComputePass("Surface Cache Screen Lookup", out ScreenIrradianceLookupPassData data))
{
data.VolumeTargetPos = _cache.Volume.TargetPos;
data.ClipToWorldTransform = clipToWorldTransform;
data.ThreadCount = new uint3((uint)_lowResScreenIrradiancesL0.rt.width, (uint)_lowResScreenIrradiancesL0.rt.height, 1);
data.Shader = _screenResolveLookupShader;
data.KernelIndex = _screenResolveLookupKernel;
data.ThreadGroupSize = _screenResolveLookupKernelGroupSize;
data.FullResDepths = resourceData.cameraDepthTexture;
data.FullResFlatNormals = fullResScreenFlatNormalsHandle;
data.LowResScreenIrradiancesL0 = lowResScreenIrradiancesL0Handle;
data.LowResScreenIrradiancesL10 = lowResScreenIrradiancesL10Handle;
data.LowResScreenIrradiancesL11 = lowResScreenIrradiancesL11Handle;
data.LowResScreenIrradiancesL12 = lowResScreenIrradiancesL12Handle;
data.LowResScreenNdcDepths = lowResScreenNdcDepthsHandle;
data.CellPatchIndices = _cache.Volume.CellPatchIndices;
data.PatchIrradiances = _cache.Patches.Irradiances[outputIrradianceBufferIdx];
data.PatchStatistics = _cache.Patches.Statistics;
data.CascadeOffsets = _cache.Volume.CascadeOffsetBuffer;
data.VolumeSpatialResolution = _cache.Volume.SpatialResolution;
data.VolumeVoxelMinSize = _cache.Volume.VoxelMinSize;
data.VolumeCascadeCount = _cache.Volume.CascadeCount;
data.SampleCount = volumeParams.ScreenFilteringParams.LookupSampleCount;
data.FrameIndex = _frameIdx;
builder.UseBuffer(cellAllocationMarkHandle, AccessFlags.Read);
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(fullResScreenFlatNormalsHandle, AccessFlags.Read);
builder.UseTexture(resourceData.motionVectorColor, AccessFlags.Read);
builder.UseTexture(data.LowResScreenIrradiancesL0, AccessFlags.Write);
builder.UseTexture(data.LowResScreenIrradiancesL10, AccessFlags.Write);
builder.UseTexture(data.LowResScreenIrradiancesL11, AccessFlags.Write);
builder.UseTexture(data.LowResScreenIrradiancesL12, AccessFlags.Write);
builder.UseTexture(data.LowResScreenNdcDepths, AccessFlags.Write);
builder.SetRenderFunc((ScreenIrradianceLookupPassData data, ComputeGraphContext cgContext) => LookupScreenIrradiance(data, cgContext));
}
if (_debugEnabled)
{
using (var builder = renderGraph.AddComputePass("Surface Cache Debug", out DebugPassData passData))
{
passData.ThreadCount = new uint3((uint)screenResolution.x, (uint)screenResolution.y, 1);
passData.Shader = _debugShader;
passData.KernelIndex = _debugKernel;
passData.ThreadGroupSize = _debugKernelGroupSize;
passData.ScreenDepths = resourceData.cameraDepthTexture;
passData.ScreenShadedNormals = resourceData.cameraNormalsTexture;
passData.ScreenFlatNormals = fullResScreenFlatNormalsHandle;
passData.ScreenIrradiances = fullResScreenIrradiancesHandle;
passData.PatchCellIndices = _cache.Patches.CellIndices;
passData.CellPatchIndices = _cache.Volume.CellPatchIndices;
passData.RingConfigBuffer = _cache.RingConfig.Buffer;
passData.RingConfigOffset = _cache.RingConfig.OffsetA;
passData.VolumeTargetPos = _cache.Volume.TargetPos;
passData.PatchIrradiances = _cache.Patches.Irradiances[outputIrradianceBufferIdx];
passData.PatchGeometries = _cache.Patches.Geometries;
passData.PatchStatistics = _cache.Patches.Statistics;
passData.VolumeSpatialResolution = _cache.Volume.SpatialResolution;
passData.VolumeVoxelMinSize = _cache.Volume.VoxelMinSize;
passData.VolumeCascadeCount = _cache.Volume.CascadeCount;
passData.CascadeOffsets = _cache.Volume.CascadeOffsetBuffer;
passData.ViewMode = _debugViewMode;
passData.FrameIndex = _frameIdx;
passData.ShowSamplePosition = _debugShowSamplePosition;
passData.ClipToWorldTransform = clipToWorldTransform;
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(resourceData.cameraNormalsTexture, AccessFlags.Read);
builder.UseTexture(fullResScreenFlatNormalsHandle, AccessFlags.Read);
builder.UseTexture(passData.ScreenIrradiances, AccessFlags.Write);
builder.SetRenderFunc((DebugPassData data, ComputeGraphContext cgContext) => RenderDebug(data, cgContext));
}
}
else
{
using (var builder = renderGraph.AddComputePass("Surface Cache Screen Upsampling", out ScreenIrradianceUpsamplingPassData data))
{
data.ClipToWorldTransform = clipToWorldTransform;
data.FullResThreadCount = new uint3((uint)screenResolution.x, (uint)screenResolution.y, 1);
data.Shader = _screenResolveUpsamplingShader;
data.KernelIndex = _screenResolveUpsamplingKernel;
data.ThreadGroupSize = _screenResolveUpsamplingKernelGroupSize;
data.FullResDepths = resourceData.cameraDepthTexture;
data.FullResShadedNormals = resourceData.cameraNormalsTexture;
data.FullResFlatNormals = fullResScreenFlatNormalsHandle;
data.LowResScreenIrradiancesL0 = lowResScreenIrradiancesL0Handle;
data.LowResScreenIrradiancesL10 = lowResScreenIrradiancesL10Handle;
data.LowResScreenIrradiancesL11 = lowResScreenIrradiancesL11Handle;
data.LowResScreenIrradiancesL12 = lowResScreenIrradiancesL12Handle;
data.FullResIrradiances = fullResScreenIrradiancesHandle;
data.FilterRadius = volumeParams.ScreenFilteringParams.UpsamplingKernelSize;
data.SampleCount = volumeParams.ScreenFilteringParams.UpsamplingSampleCount;
builder.UseTexture(data.FullResIrradiances, AccessFlags.Write);
builder.UseTexture(resourceData.cameraDepthTexture, AccessFlags.Read);
builder.UseTexture(resourceData.cameraNormalsTexture, AccessFlags.Read);
builder.UseTexture(fullResScreenFlatNormalsHandle, AccessFlags.Read);
builder.UseTexture(resourceData.motionVectorColor, AccessFlags.Read);
builder.UseTexture(data.LowResScreenIrradiancesL0, AccessFlags.Read);
builder.UseTexture(data.LowResScreenIrradiancesL10, AccessFlags.Read);
builder.UseTexture(data.LowResScreenIrradiancesL11, AccessFlags.Read);
builder.UseTexture(data.LowResScreenIrradiancesL12, AccessFlags.Read);
builder.UseBuffer(cellAllocationMarkHandle, AccessFlags.Read);
builder.SetRenderFunc((ScreenIrradianceUpsamplingPassData data, ComputeGraphContext cgContext) => UpsampleScreenIrradiance(data, cgContext));
}
}
resourceData.irradianceTexture = fullResScreenIrradiancesHandle;
_frameIdx++;
_prevClipToWorldTransform = clipToWorldTransform;
}
static void UpdateWorld(WorldUpdatePassData data, UnsafeGraphContext graphCtx, ref GraphicsBuffer scratch)
{
var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd);
data.World.Commit(cmd, ref scratch, data.EnvCubemapResolution, data.Sun, out bool viewAndProjectionMatricesChanged);
if (viewAndProjectionMatricesChanged)
{
cmd.SetViewProjectionMatrices(data.RestoreViewMatrix, data.RestoreProjectionMatrix);
}
}
static void LookupScreenIrradiance(ScreenIrradianceLookupPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ResultL0, data.LowResScreenIrradiancesL0);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ResultL10, data.LowResScreenIrradiancesL10);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ResultL11, data.LowResScreenIrradiancesL11);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ResultL12, data.LowResScreenIrradiancesL12);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ResultNdcDepths, data.LowResScreenNdcDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenDepths, data.FullResDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenFlatNormals, data.FullResFlatNormals);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._VolumeCascadeOffsets, data.CascadeOffsets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeCascadeCount, (int)data.VolumeCascadeCount);
cmd.SetComputeIntParam(shader, ShaderIDs._SampleCount, (int)data.SampleCount);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIndex);
cmd.SetComputeFloatParam(shader, ShaderIDs._VolumeVoxelMinSize, data.VolumeVoxelMinSize);
cmd.SetComputeMatrixParam(shader, ShaderIDs._ClipToWorldTransform, data.ClipToWorldTransform);
cmd.SetComputeVectorParam(shader, ShaderIDs._VolumeTargetPos, data.VolumeTargetPos);
uint3 groupCount = DivUp(data.ThreadCount, data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void UpsampleScreenIrradiance(ScreenIrradianceUpsamplingPassData passData, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = passData.Shader;
var kernelIndex = passData.KernelIndex;
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._FullResIrradiances, passData.FullResIrradiances);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._FullResDepths, passData.FullResDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._FullResFlatNormals, passData.FullResFlatNormals);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._FullResShadedNormals, passData.FullResShadedNormals);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._LowResIrradiancesL0, passData.LowResScreenIrradiancesL0);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._LowResIrradiancesL10, passData.LowResScreenIrradiancesL10);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._LowResIrradiancesL11, passData.LowResScreenIrradiancesL11);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._LowResIrradiancesL12, passData.LowResScreenIrradiancesL12);
cmd.SetComputeFloatParam(shader, ShaderIDs._FilterRadius, passData.FilterRadius);
cmd.SetComputeIntParam(shader, ShaderIDs._SampleCount, (int)passData.SampleCount);
cmd.SetComputeMatrixParam(shader, ShaderIDs._ClipToWorldTransform, passData.ClipToWorldTransform);
uint3 groupCount = DivUp(passData.FullResThreadCount, passData.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void ResolveFlatNormals(FlatNormalResolutionPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenDepths, data.ScreenDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenFlatNormals, data.ScreenFlatNormals);
cmd.SetComputeMatrixParam(shader, ShaderIDs._ClipToWorldTransform, data.ClipToWorldTransform);
uint3 groupCount = DivUp(data.ThreadCount, data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void AllocatePatches(PatchAllocationPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._CurrentFullResScreenDepths, data.ScreenDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._CurrentFullResScreenFlatNormals, data.ScreenFlatNormals);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._CurrentFullResScreenMotionVectors, data.ScreenMotionVectors);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._PreviousLowResScreenIrradiancesL0, data.LowResScreenIrradiancesL0);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._PreviousLowResScreenIrradiancesL10, data.LowResScreenIrradiancesL10);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._PreviousLowResScreenIrradiancesL11, data.LowResScreenIrradiancesL11);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._PreviousLowResScreenIrradiancesL12, data.LowResScreenIrradiancesL12);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._PreviousLowResScreenNdcDepths, data.LowResScreenNdcDepths);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellAllocationMarks, data.CellAllocationMarks);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
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._PatchCellIndices, data.PatchCellIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._VolumeCascadeOffsets, data.CascadeOffsets);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIdx);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeCascadeCount, (int)data.VolumeCascadeCount);
cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset);
cmd.SetComputeIntParams(shader, ShaderIDs._FullResPixelOffset, (int)data.FullResPixelOffset.x, (int)data.FullResPixelOffset.y);
cmd.SetComputeIntParams(shader, ShaderIDs._LowResScreenSize, (int)data.LowResScreenSize.x, (int)data.LowResScreenSize.y);
cmd.SetComputeIntParam(shader, ShaderIDs._UseMotionVectorSeeding, data.UseMotionVectorSeeding ? 1 : 0);
cmd.SetComputeFloatParam(shader, ShaderIDs._VolumeVoxelMinSize, data.VoxelMinSize);
cmd.SetComputeMatrixParam(shader, ShaderIDs._CurrentClipToWorldTransform, data.CurrentClipToWorldTransform);
cmd.SetComputeMatrixParam(shader, ShaderIDs._PreviousClipToWorldTransform, data.PreviousClipToWorldTransform);
cmd.SetComputeVectorParam(shader, ShaderIDs._VolumeTargetPos, data.VolumeTargetPos);
uint3 groupCount = DivUp(data.ThreadCount, data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
static void RenderDebug(DebugPassData data, ComputeGraphContext cgContext)
{
var cmd = cgContext.cmd;
var shader = data.Shader;
var kernelIndex = data.KernelIndex;
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._Result, data.ScreenIrradiances);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenDepths, data.ScreenDepths);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenShadedNormals, data.ScreenShadedNormals);
cmd.SetComputeTextureParam(shader, kernelIndex, ShaderIDs._ScreenFlatNormals, data.ScreenFlatNormals);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances, data.PatchIrradiances);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._VolumeCascadeOffsets, data.CascadeOffsets);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices);
cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution);
cmd.SetComputeIntParam(shader, ShaderIDs._VolumeCascadeCount, (int)data.VolumeCascadeCount);
cmd.SetComputeIntParam(shader, ShaderIDs._ViewMode, (int)data.ViewMode);
cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIndex);
cmd.SetComputeIntParam(shader, ShaderIDs._ShowSamplePosition, data.ShowSamplePosition ? 1 : 0);
cmd.SetComputeFloatParam(shader, ShaderIDs._VolumeVoxelMinSize, data.VolumeVoxelMinSize);
cmd.SetComputeFloatParam(shader, ShaderIDs._RingConfigOffset, data.RingConfigOffset);
cmd.SetComputeVectorParam(shader, ShaderIDs._VolumeTargetPos, data.VolumeTargetPos);
cmd.SetComputeMatrixParam(shader, ShaderIDs._ClipToWorldTransform, data.ClipToWorldTransform);
uint3 groupCount = DivUp(data.ThreadCount, data.ThreadGroupSize);
cmd.DispatchCompute(shader, kernelIndex, (int)groupCount.x, (int)groupCount.y, 1);
}
private static uint3 DivUp(uint3 x, uint3 y) => (x + y - 1) / y;
private static uint2 DivUp(uint2 x, uint2 y) => (x + y - 1) / y;
}
private SurfaceCachePass _pass;
private RayTracingContext _rtContext;
[SerializeField] private ParameterSet _parameterSet = new ParameterSet();
// Main parameter set containing advanced and debug settings.
[Serializable]
class ParameterSet
{
// Debug settings (will be moved to rendering debugger in the future)
public bool DebugEnabled = false;
public DebugViewMode_ DebugViewMode = DebugViewMode_.CellIndex;
public bool DebugShowSamplePosition = false;
}
void ClearResources()
{
_pass?.Dispose();
_pass = null;
_rtContext?.Dispose();
_rtContext = null;
}
// Parameters extracted from Volume override each frame
private struct VolumeParameterSet
{
public SurfaceCacheEstimationParameterSet EstimationParams;
public SurfaceCachePatchFilteringParameterSet PatchFilteringParams;
public SurfaceCacheScreenFilteringParameterSet ScreenFilteringParams;
public bool CascadeMovement;
public float VolumeSize;
public uint VolumeResolution;
public uint VolumeCascadeCount;
public uint DefragCount;
public uint RenderingLayerMask;
}
bool ResourcesCreated()
{
return _pass != null;
}
public override void Create()
{
ClearResources();
if (!isActive)
return;
#if UNITY_EDITOR
if (CheckStaticBatchingStatus())
return;
#endif
var rtBackend = SystemInfo.supportsRayTracing
? RayTracingBackend.Hardware
: RayTracingBackend.Compute;
{
var resources = new RayTracingResources();
#if UNITY_EDITOR
resources.Load();
#else
resources.LoadFromRenderPipelineResources();
#endif
_rtContext = new RayTracingContext(rtBackend, resources);
}
var universalRenderPipelineResources = GraphicsSettings.GetRenderPipelineSettings<SurfaceCacheRenderPipelineResourceSet>();
Debug.Assert(universalRenderPipelineResources != null);
var worldResources = new WorldResourceSet();
var worldLoadResult = worldResources.LoadFromRenderPipelineResources();
Debug.Assert(worldLoadResult);
var coreRpResources = GraphicsSettings.GetRenderPipelineSettings<Rendering.SurfaceCacheRenderPipelineResourceSet>();
Debug.Assert(coreRpResources != null);
var coreResources = new Rendering.SurfaceCacheResourceSet((uint)SystemInfo.computeSubGroupSize);
Object punctualLightSamplingUnifiedObj;
Object estimationUnifiedObj;
if (_rtContext.BackendType == RayTracingBackend.Compute)
{
punctualLightSamplingUnifiedObj = coreRpResources.punctualLightSamplingComputeShader;
estimationUnifiedObj = coreRpResources.estimationComputeShader;
}
else
{
punctualLightSamplingUnifiedObj = coreRpResources.punctualLightSamplingRayTracingShader;
estimationUnifiedObj = coreRpResources.estimationRayTracingShader;
}
IRayTracingShader punctualLightSamplingShader = _rtContext.CreateRayTracingShader(punctualLightSamplingUnifiedObj);
IRayTracingShader estimationShader = _rtContext.CreateRayTracingShader(estimationUnifiedObj);
coreResources.Load(
coreRpResources.scrollingShader,
coreRpResources.evictionShader,
coreRpResources.patchAllocationShader,
coreRpResources.spatialFilteringShader,
coreRpResources.temporalFilteringShader,
coreRpResources.defragShader,
punctualLightSamplingShader,
estimationShader);
// Use defaults for initial volume configuration; runtime values come from the Volume override per-frame
var volParams = new SurfaceCacheVolumeParameterSet
{
Resolution = DEFAULT_VOLUME_RESOLUTION,
Size = DEFAULT_VOLUME_SIZE,
CascadeCount = DEFAULT_VOLUME_CASCADE_COUNT
};
_pass = new SurfaceCachePass(
_rtContext,
coreResources,
worldResources,
universalRenderPipelineResources.allocationShader,
universalRenderPipelineResources.screenResolveLookupShader,
universalRenderPipelineResources.screenResolveUpsamplingShader,
universalRenderPipelineResources.debugShader,
universalRenderPipelineResources.flatNormalResolutionShader,
universalRenderPipelineResources.fallbackMaterial,
_parameterSet.DebugEnabled,
_parameterSet.DebugViewMode,
_parameterSet.DebugShowSamplePosition,
volParams);
_pass.renderPassEvent = RenderPassEvent.AfterRenderingPrePasses + 1;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
#if UNITY_EDITOR
if (CheckStaticBatchingStatus())
return;
#endif
var cameraType = renderingData.cameraData.cameraType;
if (cameraType != CameraType.Game && cameraType != CameraType.SceneView)
return;
var surfaceCacheOverride = VolumeManager.instance.stack.GetComponent<SurfaceCacheGIVolumeOverride>();
bool enabled = surfaceCacheOverride == null || surfaceCacheOverride.enabled.value;
if (!enabled)
{
if (ResourcesCreated())
ClearResources();
}
else
{
// Resources may be absent here after being released while disabled, or after static
// batching was disabled following feature creation.
if (!ResourcesCreated())
Create();
// Create() leaves _pass null when the feature is inactive.
if (!ResourcesCreated())
return;
ScriptableRenderPassInput inputs = ScriptableRenderPassInput.Depth | ScriptableRenderPassInput.Normal;
if (UseMotionVectorPatchSeeding(renderingData.cameraData.cameraType))
{
inputs |= ScriptableRenderPassInput.Motion;
}
_pass.ConfigureInput(inputs);
renderer.EnqueuePass(_pass);
}
}
protected override void Dispose(bool disposing)
{
ClearResources();
base.Dispose(disposing);
}
#if UNITY_EDITOR
bool _staticBatchingEnabled;
bool CheckStaticBatchingStatus()
{
bool previousStatus = _staticBatchingEnabled;
_staticBatchingEnabled = UnityEditor.PlayerSettings.GetStaticBatchingForPlatform(UnityEditor.EditorUserBuildSettings.activeBuildTarget);
if (_staticBatchingEnabled && _staticBatchingEnabled != previousStatus)
Debug.LogError(k_StaticBatchingErrorMesssage);
return _staticBatchingEnabled;
}
#endif
}
}
#endif