You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1513 lines
90 KiB

using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.HighDefinition
{
public partial class HDRenderPipeline
{
private static LocalKeyword s_BigTileVolumetricLightListKeyword;
struct LightingBuffers
{
public TextureHandle sssBuffer;
public TextureHandle diffuseLightingBuffer;
public TextureHandle ambientOcclusionBuffer;
public TextureHandle ssrLightingBuffer;
public TextureHandle ssgiLightingBuffer;
public TextureHandle contactShadowsBuffer;
public TextureHandle screenspaceShadowBuffer;
}
static LightingBuffers ReadLightingBuffers(in LightingBuffers buffers, RenderGraphBuilder builder)
{
var result = new LightingBuffers();
// We only read those buffers because sssBuffer and diffuseLightingBuffer our just output of the lighting process, not inputs.
result.ambientOcclusionBuffer = builder.ReadTexture(buffers.ambientOcclusionBuffer);
result.ssrLightingBuffer = builder.ReadTexture(buffers.ssrLightingBuffer);
result.ssgiLightingBuffer = builder.ReadTexture(buffers.ssgiLightingBuffer);
result.contactShadowsBuffer = builder.ReadTexture(buffers.contactShadowsBuffer);
result.screenspaceShadowBuffer = builder.ReadTexture(buffers.screenspaceShadowBuffer);
return result;
}
static void BindGlobalLightingBuffers(in LightingBuffers buffers, CommandBuffer cmd)
{
cmd.SetGlobalTexture(HDShaderIDs._AmbientOcclusionTexture, buffers.ambientOcclusionBuffer);
cmd.SetGlobalTexture(HDShaderIDs._SsrLightingTexture, buffers.ssrLightingBuffer);
cmd.SetGlobalTexture(HDShaderIDs._IndirectDiffuseTexture, buffers.ssgiLightingBuffer);
cmd.SetGlobalTexture(HDShaderIDs._ContactShadowTexture, buffers.contactShadowsBuffer);
cmd.SetGlobalTexture(HDShaderIDs._ScreenSpaceShadowsTexture, buffers.screenspaceShadowBuffer);
}
static void BindGlobalThicknessBuffers(TextureHandle thicknessTexture, GraphicsBuffer thicknessReindexMap, CommandBuffer cmd)
{
cmd.SetGlobalTexture(HDShaderIDs._ThicknessTexture, thicknessTexture);
cmd.SetGlobalBuffer(HDShaderIDs._ThicknessReindexMap, thicknessReindexMap);
}
static void BindDefaultTexturesLightingBuffers(RenderGraphDefaultResources defaultResources, CommandBuffer cmd)
{
cmd.SetGlobalTexture(HDShaderIDs._AmbientOcclusionTexture, defaultResources.blackTextureXR);
cmd.SetGlobalTexture(HDShaderIDs._SsrLightingTexture, defaultResources.blackTextureXR);
cmd.SetGlobalTexture(HDShaderIDs._IndirectDiffuseTexture, defaultResources.blackTextureXR);
cmd.SetGlobalTexture(HDShaderIDs._ContactShadowTexture, defaultResources.blackUIntTextureXR);
cmd.SetGlobalTexture(HDShaderIDs._ScreenSpaceShadowsTexture, defaultResources.blackTextureXR);
}
class BuildGPULightListPassData
{
// Common
public int totalLightCount; // Regular + Env + Decal + Local Volumetric Fog
public int viewCount;
public bool runLightList;
public bool clearLightLists;
public bool enableFeatureVariants;
public bool computeMaterialVariants;
public bool computeLightVariants;
public bool skyEnabled;
public LightList lightList;
public bool canClearLightList;
public int directionalLightCount;
// Clear Light lists
public ComputeShader clearLightListCS;
public int clearLightListKernel;
// Screen Space AABBs
public ComputeShader screenSpaceAABBShader;
public int screenSpaceAABBKernel;
// Big Tile
public ComputeShader bigTilePrepassShader;
public int bigTilePrepassKernel;
public bool runBigTilePrepass;
public int numBigTilesX, numBigTilesY;
public bool supportsVolumetric;
// FPTL
public ComputeShader buildPerTileLightListShader;
public int buildPerTileLightListKernel;
public bool runFPTL;
public int numTilesFPTLX;
public int numTilesFPTLY;
public int numTilesFPTL;
// Cluster
public ComputeShader buildPerVoxelLightListShader;
public ComputeShader clearClusterAtomicIndexShader;
public int buildPerVoxelLightListKernel;
public int numTilesClusterX;
public int numTilesClusterY;
public bool clusterNeedsDepth;
// Build dispatch indirect
public ComputeShader buildMaterialFlagsShader;
public ComputeShader clearDispatchIndirectShader;
public ComputeShader buildDispatchIndirectShader;
public ShaderVariablesLightList lightListCB;
public TextureHandle depthBuffer;
public TextureHandle stencilTexture;
public TextureHandle[] gBuffer = new TextureHandle[RenderGraph.kMaxMRTCount];
public int gBufferCount;
// Buffers filled with the CPU outside of render graph.
public BufferHandle convexBoundsBuffer;
public BufferHandle AABBBoundsBuffer;
// Transient buffers that are not used outside of BuildGPULight list so they don't need to go outside the pass.
public BufferHandle globalLightListAtomic;
public BufferHandle lightVolumeDataBuffer;
public BuildGPULightListOutput output = new BuildGPULightListOutput();
}
internal struct BuildGPULightListOutput
{
// Tile
public BufferHandle lightList;
public BufferHandle tileList;
public BufferHandle tileFeatureFlags;
public BufferHandle dispatchIndirectBuffer;
// Big Tile
public BufferHandle bigTileLightList;
public BufferHandle bigTileVolumetricLightList;
// Cluster
public BufferHandle perVoxelOffset;
public BufferHandle perVoxelLightLists;
public BufferHandle perTileLogBaseTweak;
}
static void ClearLightList(BuildGPULightListPassData data, CommandBuffer cmd, GraphicsBuffer bufferToClear)
{
cmd.SetComputeBufferParam(data.clearLightListCS, data.clearLightListKernel, HDShaderIDs._LightListToClear, bufferToClear);
Vector2 countAndOffset = new Vector2Int(bufferToClear.count, 0);
int groupSize = 64;
int totalNumberOfGroupsNeeded = (bufferToClear.count + groupSize - 1) / groupSize;
const int maxAllowedGroups = 65535;
// On higher resolutions we might end up with more than 65535 group which is not allowed, so we need to to have multiple dispatches.
int i = 0;
while (totalNumberOfGroupsNeeded > 0)
{
countAndOffset.y = maxAllowedGroups * i;
cmd.SetComputeVectorParam(data.clearLightListCS, HDShaderIDs._LightListEntriesAndOffset, countAndOffset);
int currGroupCount = Math.Min(maxAllowedGroups, totalNumberOfGroupsNeeded);
cmd.DispatchCompute(data.clearLightListCS, data.clearLightListKernel, currGroupCount, 1, 1);
totalNumberOfGroupsNeeded -= currGroupCount;
i++;
}
}
static void ClearLightLists(BuildGPULightListPassData data, CommandBuffer cmd)
{
if (data.clearLightLists)
{
// Note we clear the whole content and not just the header since it is fast enough, happens only in one frame and is a bit more robust
// to changes to the inner workings of the lists.
// Also, we clear all the lists and to be resilient to changes in pipeline.
if (data.runBigTilePrepass)
{
ClearLightList(data, cmd, data.output.bigTileLightList);
if (data.supportsVolumetric)
ClearLightList(data, cmd, data.output.bigTileVolumetricLightList);
}
if (data.canClearLightList) // This can happen when we dont have a GPULight list builder and a light list instantiated.
ClearLightList(data, cmd, data.output.lightList);
ClearLightList(data, cmd, data.output.perVoxelOffset);
}
}
// generate screen-space AABBs (used for both fptl and clustered).
static void GenerateLightsScreenSpaceAABBs(BuildGPULightListPassData data, CommandBuffer cmd)
{
if (data.totalLightCount != 0)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.GenerateLightAABBs)))
{
// With XR single-pass, we have one set of light bounds per view to iterate over (bounds are in view space for each view)
cmd.SetComputeBufferParam(data.screenSpaceAABBShader, data.screenSpaceAABBKernel, HDShaderIDs.g_data, data.convexBoundsBuffer);
cmd.SetComputeBufferParam(data.screenSpaceAABBShader, data.screenSpaceAABBKernel, HDShaderIDs.g_vBoundsBuffer, data.AABBBoundsBuffer);
ConstantBuffer.Push(cmd, data.lightListCB, data.screenSpaceAABBShader, HDShaderIDs._ShaderVariablesLightList);
const int threadsPerLight = 4; // Shader: THREADS_PER_LIGHT (4)
const int threadsPerGroup = 64; // Shader: THREADS_PER_GROUP (64)
int groupCount = HDUtils.DivRoundUp(data.totalLightCount * threadsPerLight, threadsPerGroup);
cmd.DispatchCompute(data.screenSpaceAABBShader, data.screenSpaceAABBKernel, groupCount, data.viewCount, 1);
}
}
}
// enable coarse 2D pass on 64x64 tiles (used for both fptl and clustered).
static void BigTilePrepass(BuildGPULightListPassData data, CommandBuffer cmd)
{
if (data.runLightList && data.runBigTilePrepass)
{
cmd.SetComputeBufferParam(data.bigTilePrepassShader, data.bigTilePrepassKernel, HDShaderIDs.g_vLightList, data.output.bigTileLightList);
cmd.SetKeyword(data.bigTilePrepassShader, s_BigTileVolumetricLightListKeyword, data.supportsVolumetric);
if (data.supportsVolumetric)
cmd.SetComputeBufferParam(data.bigTilePrepassShader, data.bigTilePrepassKernel, HDShaderIDs.g_vVolumetricLightList, data.output.bigTileVolumetricLightList);
cmd.SetComputeBufferParam(data.bigTilePrepassShader, data.bigTilePrepassKernel, HDShaderIDs.g_vBoundsBuffer, data.AABBBoundsBuffer);
cmd.SetComputeBufferParam(data.bigTilePrepassShader, data.bigTilePrepassKernel, HDShaderIDs._LightVolumeData, data.lightVolumeDataBuffer);
cmd.SetComputeBufferParam(data.bigTilePrepassShader, data.bigTilePrepassKernel, HDShaderIDs.g_data, data.convexBoundsBuffer);
ConstantBuffer.Push(cmd, data.lightListCB, data.bigTilePrepassShader, HDShaderIDs._ShaderVariablesLightList);
cmd.DispatchCompute(data.bigTilePrepassShader, data.bigTilePrepassKernel, data.numBigTilesX, data.numBigTilesY, data.viewCount);
}
}
static void BuildPerTileLightList(BuildGPULightListPassData data, ref bool tileFlagsWritten, CommandBuffer cmd)
{
// optimized for opaques only
if (data.runLightList && data.runFPTL)
{
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_vBoundsBuffer, data.AABBBoundsBuffer);
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs._LightVolumeData, data.lightVolumeDataBuffer);
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_data, data.convexBoundsBuffer);
cmd.SetComputeTextureParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_depth_tex, data.depthBuffer);
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_vLightList, data.output.lightList);
if (data.runBigTilePrepass)
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_vBigTileLightList, data.output.bigTileLightList);
var localLightListCB = data.lightListCB;
if (data.enableFeatureVariants)
{
uint baseFeatureFlags = 0;
if (data.directionalLightCount > 0)
{
baseFeatureFlags |= (uint)LightFeatureFlags.Directional;
}
if (data.skyEnabled)
{
baseFeatureFlags |= (uint)LightFeatureFlags.Sky;
}
if (!data.computeMaterialVariants)
{
baseFeatureFlags |= LightDefinitions.s_MaterialFeatureMaskFlags;
}
localLightListCB.g_BaseFeatureFlags = baseFeatureFlags;
cmd.SetComputeBufferParam(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, HDShaderIDs.g_TileFeatureFlags, data.output.tileFeatureFlags);
tileFlagsWritten = true;
}
ConstantBuffer.Push(cmd, localLightListCB, data.buildPerTileLightListShader, HDShaderIDs._ShaderVariablesLightList);
cmd.DispatchCompute(data.buildPerTileLightListShader, data.buildPerTileLightListKernel, data.numTilesFPTLX, data.numTilesFPTLY, data.viewCount);
}
}
static void VoxelLightListGeneration(BuildGPULightListPassData data, CommandBuffer cmd)
{
if (data.runLightList)
{
// clear atomic offset index
cmd.SetComputeBufferParam(data.clearClusterAtomicIndexShader, s_ClearVoxelAtomicKernel, HDShaderIDs.g_LayeredSingleIdxBuffer, data.globalLightListAtomic);
cmd.DispatchCompute(data.clearClusterAtomicIndexShader, s_ClearVoxelAtomicKernel, 1, 1, 1);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, s_ClearVoxelAtomicKernel, HDShaderIDs.g_LayeredSingleIdxBuffer, data.globalLightListAtomic);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_vLayeredLightList, data.output.perVoxelLightLists);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_LayeredOffset, data.output.perVoxelOffset);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_LayeredSingleIdxBuffer, data.globalLightListAtomic);
if (data.runBigTilePrepass)
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_vBigTileLightList, data.output.bigTileLightList);
if (data.clusterNeedsDepth)
{
cmd.SetComputeTextureParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_depth_tex, data.depthBuffer);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_logBaseBuffer, data.output.perTileLogBaseTweak);
}
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_vBoundsBuffer, data.AABBBoundsBuffer);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs._LightVolumeData, data.lightVolumeDataBuffer);
cmd.SetComputeBufferParam(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, HDShaderIDs.g_data, data.convexBoundsBuffer);
ConstantBuffer.Push(cmd, data.lightListCB, data.buildPerVoxelLightListShader, HDShaderIDs._ShaderVariablesLightList);
cmd.DispatchCompute(data.buildPerVoxelLightListShader, data.buildPerVoxelLightListKernel, data.numTilesClusterX, data.numTilesClusterY, data.viewCount);
}
}
static void BuildDispatchIndirectArguments(BuildGPULightListPassData data, bool tileFlagsWritten, CommandBuffer cmd)
{
if (data.enableFeatureVariants)
{
// We need to touch up the tile flags if we need material classification or, if disabled, to patch up for missing flags during the skipped light tile gen
bool needModifyingTileFeatures = !tileFlagsWritten || data.computeMaterialVariants;
if (needModifyingTileFeatures)
{
int buildMaterialFlagsKernel = s_BuildMaterialFlagsWriteKernel;
data.buildMaterialFlagsShader.shaderKeywords = null;
if (tileFlagsWritten && data.computeLightVariants)
{
data.buildMaterialFlagsShader.EnableKeyword("USE_OR");
}
uint baseFeatureFlags = 0;
if (!data.computeLightVariants)
{
baseFeatureFlags |= LightDefinitions.s_LightFeatureMaskFlags;
}
// If we haven't run the light list building, we are missing some basic lighting flags.
if (!tileFlagsWritten)
{
if (data.directionalLightCount > 0)
{
baseFeatureFlags |= (uint)LightFeatureFlags.Directional;
}
if (data.skyEnabled)
{
baseFeatureFlags |= (uint)LightFeatureFlags.Sky;
}
if (!data.computeMaterialVariants)
{
baseFeatureFlags |= LightDefinitions.s_MaterialFeatureMaskFlags;
}
}
var localLightListCB = data.lightListCB;
localLightListCB.g_BaseFeatureFlags = baseFeatureFlags;
cmd.SetComputeBufferParam(data.buildMaterialFlagsShader, buildMaterialFlagsKernel, HDShaderIDs.g_TileFeatureFlags, data.output.tileFeatureFlags);
for (int i = 0; i < data.gBufferCount; ++i)
cmd.SetComputeTextureParam(data.buildMaterialFlagsShader, buildMaterialFlagsKernel, HDShaderIDs._GBufferTexture[i], data.gBuffer[i]);
RTHandle stencilTexture = data.stencilTexture;
if (stencilTexture?.rt != null && stencilTexture.rt.stencilFormat != GraphicsFormat.None)
{
cmd.SetComputeTextureParam(data.buildMaterialFlagsShader, buildMaterialFlagsKernel, HDShaderIDs._StencilTexture, data.stencilTexture, 0, RenderTextureSubElement.Stencil);
}
else // We are accessing MSAA resolved version or default black texture and not the depth stencil buffer directly.
{
cmd.SetComputeTextureParam(data.buildMaterialFlagsShader, buildMaterialFlagsKernel, HDShaderIDs._StencilTexture, data.stencilTexture);
}
ConstantBuffer.Push(cmd, localLightListCB, data.buildMaterialFlagsShader, HDShaderIDs._ShaderVariablesLightList);
cmd.DispatchCompute(data.buildMaterialFlagsShader, buildMaterialFlagsKernel, data.numTilesFPTLX, data.numTilesFPTLY, data.viewCount);
}
// clear dispatch indirect buffer
cmd.SetComputeBufferParam(data.clearDispatchIndirectShader, s_ClearDispatchIndirectKernel, HDShaderIDs.g_DispatchIndirectBuffer, data.output.dispatchIndirectBuffer);
cmd.DispatchCompute(data.clearDispatchIndirectShader, s_ClearDispatchIndirectKernel, 1, 1, 1);
// add tiles to indirect buffer
cmd.SetComputeBufferParam(data.buildDispatchIndirectShader, s_BuildIndirectKernel, HDShaderIDs.g_DispatchIndirectBuffer, data.output.dispatchIndirectBuffer);
cmd.SetComputeBufferParam(data.buildDispatchIndirectShader, s_BuildIndirectKernel, HDShaderIDs.g_TileList, data.output.tileList);
cmd.SetComputeBufferParam(data.buildDispatchIndirectShader, s_BuildIndirectKernel, HDShaderIDs.g_TileFeatureFlags, data.output.tileFeatureFlags);
cmd.SetComputeIntParam(data.buildDispatchIndirectShader, HDShaderIDs.g_NumTiles, data.numTilesFPTL);
cmd.SetComputeIntParam(data.buildDispatchIndirectShader, HDShaderIDs.g_NumTilesX, data.numTilesFPTLX);
// Round on k_ThreadGroupOptimalSize so we have optimal thread for buildDispatchIndirectShader kernel
cmd.DispatchCompute(data.buildDispatchIndirectShader, s_BuildIndirectKernel, (data.numTilesFPTL + k_ThreadGroupOptimalSize - 1) / k_ThreadGroupOptimalSize, 1, data.viewCount);
}
}
unsafe void PrepareBuildGPULightListPassData(
RenderGraph renderGraph,
RenderGraphBuilder builder,
HDCamera hdCamera,
TileAndClusterData tileAndClusterData,
ref ShaderVariablesLightList constantBuffer,
int totalLightCount,
TextureHandle depthStencilBuffer,
TextureHandle stencilBufferCopy,
GBufferOutput gBuffer,
BuildGPULightListPassData passData)
{
var camera = hdCamera.camera;
var w = (int)hdCamera.screenSize.x;
var h = (int)hdCamera.screenSize.y;
// Fill the shared constant buffer.
// We don't fill directly the one in the parameter struct because we will need those parameters for volumetric lighting as well.
ref var cb = ref constantBuffer;
var temp = new Matrix4x4();
temp.SetRow(0, new Vector4(0.5f * w, 0.0f, 0.0f, 0.5f * w));
temp.SetRow(1, new Vector4(0.0f, 0.5f * h, 0.0f, 0.5f * h));
temp.SetRow(2, new Vector4(0.0f, 0.0f, 0.5f, 0.5f));
temp.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
// camera to screen matrix (and it's inverse)
for (int viewIndex = 0; viewIndex < hdCamera.viewCount; ++viewIndex)
{
var proj = hdCamera.xr.enabled ? hdCamera.xr.GetProjMatrix(viewIndex) : camera.projectionMatrix;
// Note: we need to take into account the TAA jitter when indexing the light list
proj = hdCamera.RequiresCameraJitter() ? hdCamera.GetJitteredProjectionMatrix(proj) : proj;
m_LightListProjMatrices[viewIndex] = proj * s_FlipMatrixLHSRHS;
var tempMatrix = temp * m_LightListProjMatrices[viewIndex];
var invTempMatrix = tempMatrix.inverse;
for (int i = 0; i < 16; ++i)
{
cb.g_mScrProjectionArr[viewIndex * 16 + i] = tempMatrix[i];
cb.g_mInvScrProjectionArr[viewIndex * 16 + i] = invTempMatrix[i];
}
}
// camera to screen matrix (and it's inverse)
for (int viewIndex = 0; viewIndex < hdCamera.viewCount; ++viewIndex)
{
temp.SetRow(0, new Vector4(1.0f, 0.0f, 0.0f, 0.0f));
temp.SetRow(1, new Vector4(0.0f, 1.0f, 0.0f, 0.0f));
temp.SetRow(2, new Vector4(0.0f, 0.0f, 0.5f, 0.5f));
temp.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
var tempMatrix = temp * m_LightListProjMatrices[viewIndex];
var invTempMatrix = tempMatrix.inverse;
for (int i = 0; i < 16; ++i)
{
cb.g_mProjectionArr[viewIndex * 16 + i] = tempMatrix[i];
cb.g_mInvProjectionArr[viewIndex * 16 + i] = invTempMatrix[i];
}
}
var decalDatasCount = Math.Min(DecalSystem.m_DecalDatasCount, m_MaxDecalsOnScreen);
cb.g_iNrVisibLights = totalLightCount;
cb.g_screenSize = hdCamera.screenSize; // TODO remove and use global one.
cb.g_viDimensions = new Vector2Int((int)hdCamera.screenSize.x, (int)hdCamera.screenSize.y);
cb.g_isOrthographic = camera.orthographic ? 1u : 0u;
cb.g_BaseFeatureFlags = 0; // Filled for each individual pass.
cb.g_iNumSamplesMSAA = (int)hdCamera.msaaSamples;
cb._EnvLightIndexShift = (uint)m_GpuLightsBuilder.lightsCount;
cb._DecalIndexShift = (uint)(m_GpuLightsBuilder.lightsCount + m_lightList.envLights.Count);
// Copy the constant buffer into the parameter struct.
passData.lightListCB = cb;
passData.totalLightCount = totalLightCount;
passData.runLightList = passData.totalLightCount > 0;
passData.clearLightLists = false;
// TODO RENDERGRAPH: This logic is flawed with Render Graph.
// In theory buffers memory might be reused from another usage entirely so keeping track of its "cleared" state does not represent the truth of their content.
// In practice though, when resolution stays the same, buffers will be the same reused from one frame to another
// because for now buffers are pooled based on their passData. When we do proper aliasing though, we might end up with any random chunk of memory.
// Always build the light list in XR mode to avoid issues with multi-pass
if (hdCamera.xr.enabled)
{
passData.runLightList = true;
}
else if (!passData.runLightList && !tileAndClusterData.listsAreClear)
{
passData.clearLightLists = true;
// After that, No need to clear it anymore until we start and stop running light list building.
tileAndClusterData.listsAreClear = true;
}
else if (passData.runLightList)
{
tileAndClusterData.listsAreClear = false;
}
passData.viewCount = hdCamera.viewCount;
passData.enableFeatureVariants = GetFeatureVariantsEnabled(hdCamera.frameSettings) && tileAndClusterData.hasTileBuffers;
passData.computeMaterialVariants = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ComputeMaterialVariants);
passData.computeLightVariants = hdCamera.frameSettings.IsEnabled(FrameSettingsField.ComputeLightVariants);
passData.directionalLightCount = m_GpuLightsBuilder.directionalLightCount;
passData.canClearLightList = m_GpuLightsBuilder != null && m_lightList != null;
passData.skyEnabled = m_SkyManager.IsLightingSkyValid(hdCamera);
bool isProjectionOblique = GeometryUtils.IsProjectionMatrixOblique(m_LightListProjMatrices[0]);
// Clear light lsts
passData.clearLightListCS = runtimeShaders.clearLightListsCS;
passData.clearLightListKernel = passData.clearLightListCS.FindKernel("ClearList");
// Screen space AABB
passData.screenSpaceAABBShader = buildScreenAABBShader;
passData.screenSpaceAABBKernel = 0;
// Big tile prepass
passData.runBigTilePrepass = hdCamera.frameSettings.IsEnabled(FrameSettingsField.BigTilePrepass);
passData.bigTilePrepassShader = buildPerBigTileLightListShader;
passData.bigTilePrepassKernel = s_GenListPerBigTileKernel;
passData.numBigTilesX = (w + 63) / 64;
passData.numBigTilesY = (h + 63) / 64;
passData.supportsVolumetric = currentAsset.currentPlatformRenderPipelineSettings.supportVolumetrics;
// Fptl
passData.runFPTL = hdCamera.frameSettings.fptl && tileAndClusterData.hasTileBuffers;
passData.buildPerTileLightListShader = buildPerTileLightListShader;
passData.buildPerTileLightListShader.shaderKeywords = null;
if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.BigTilePrepass))
{
passData.buildPerTileLightListShader.EnableKeyword("USE_TWO_PASS_TILED_LIGHTING");
}
if (isProjectionOblique)
{
passData.buildPerTileLightListShader.EnableKeyword("USE_OBLIQUE_MODE");
}
if (GetFeatureVariantsEnabled(hdCamera.frameSettings))
{
passData.buildPerTileLightListShader.EnableKeyword("USE_FEATURE_FLAGS");
}
passData.buildPerTileLightListKernel = s_GenListPerTileKernel;
passData.numTilesFPTLX = GetNumTileFtplX(hdCamera);
passData.numTilesFPTLY = GetNumTileFtplY(hdCamera);
passData.numTilesFPTL = passData.numTilesFPTLX * passData.numTilesFPTLY;
// Cluster
bool msaa = hdCamera.msaaEnabled;
var clustPrepassSourceIdx = hdCamera.frameSettings.IsEnabled(FrameSettingsField.BigTilePrepass) ? ClusterPrepassSource.BigTile : ClusterPrepassSource.None;
var clustDepthSourceIdx = ClusterDepthSource.NoDepth;
if (tileAndClusterData.clusterNeedsDepth)
clustDepthSourceIdx = msaa ? ClusterDepthSource.MSAA_Depth : ClusterDepthSource.Depth;
passData.buildPerVoxelLightListShader = buildPerVoxelLightListShader;
passData.clearClusterAtomicIndexShader = clearClusterAtomicIndexShader;
passData.buildPerVoxelLightListKernel = isProjectionOblique ? s_ClusterObliqueKernels[(int)clustPrepassSourceIdx, (int)clustDepthSourceIdx] : s_ClusterKernels[(int)clustPrepassSourceIdx, (int)clustDepthSourceIdx];
passData.numTilesClusterX = GetNumTileClusteredX(hdCamera);
passData.numTilesClusterY = GetNumTileClusteredY(hdCamera);
passData.clusterNeedsDepth = tileAndClusterData.clusterNeedsDepth;
// Build dispatch indirect
passData.buildMaterialFlagsShader = buildMaterialFlagsShader;
passData.clearDispatchIndirectShader = clearDispatchIndirectShader;
passData.buildDispatchIndirectShader = buildDispatchIndirectShader;
passData.buildDispatchIndirectShader.shaderKeywords = null;
// Depending on frame setting configurations we might not have written to a depth buffer yet so when executing the pass it might not be valid.
if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.OpaqueObjects))
{
passData.depthBuffer = builder.ReadTexture(depthStencilBuffer);
passData.stencilTexture = builder.ReadTexture(stencilBufferCopy);
}
else
{
passData.depthBuffer = builder.ReadTexture(renderGraph.defaultResources.blackTextureXR);
passData.stencilTexture = builder.ReadTexture(renderGraph.defaultResources.blackTextureXR);
}
if (passData.computeMaterialVariants && passData.enableFeatureVariants)
{
// When opaques are disabled, gbuffer count is zero.
// Unfortunately, compute shader will then complains some textures aren't bound, so we need to use black textures instead.
for (int i = 0; i < gBuffer.gBufferCount; ++i)
passData.gBuffer[i] = builder.ReadTexture(gBuffer.mrt[i]);
for (int i = gBuffer.gBufferCount; i < 7; ++i)
passData.gBuffer[i] = renderGraph.defaultResources.blackTextureXR;
passData.gBufferCount = 7;
}
// Here we use m_MaxViewCount/m_MaxWidthHeight to avoid always allocating buffers of different sizes for each camera.
// This way we'll be reusing them more often.
// Those buffer are filled with the CPU outside of the render graph.
passData.convexBoundsBuffer = builder.ReadBuffer(renderGraph.ImportBuffer(tileAndClusterData.convexBoundsBuffer));
passData.lightVolumeDataBuffer = builder.ReadBuffer(renderGraph.ImportBuffer(tileAndClusterData.lightVolumeDataBuffer));
passData.globalLightListAtomic = builder.CreateTransientBuffer(new BufferDesc(1, sizeof(uint)) { name = "LightListAtomic" });
passData.AABBBoundsBuffer = builder.CreateTransientBuffer(new BufferDesc(m_MaxViewCount * 2 * tileAndClusterData.maxLightCount, 4 * sizeof(float)) { name = "AABBBoundBuffer" });
var nrTilesX = (m_MaxCameraWidth + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl;
var nrTilesY = (m_MaxCameraHeight + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl;
var nrTiles = nrTilesX * nrTilesY * m_MaxViewCount;
if (tileAndClusterData.hasTileBuffers)
{
// note that nrTiles include the viewCount in allocation below
// Tile buffers
passData.output.lightList = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc((int)LightCategory.Count * InternalLightCullingDefs.s_LightDwordPerFptlTile * nrTiles, sizeof(uint)) { name = "LightList" }));
passData.output.tileList = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(LightDefinitions.s_NumFeatureVariants * nrTiles, sizeof(uint)) { name = "TileList" }));
passData.output.tileFeatureFlags = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(nrTiles, sizeof(uint)) { name = "TileFeatureFlags" }));
// DispatchIndirect: Buffer with arguments has to have three integer numbers at given argsOffset offset: number of work groups in X dimension, number of work groups in Y dimension, number of work groups in Z dimension.
// DrawProceduralIndirect: Buffer with arguments has to have four integer numbers at given argsOffset offset: vertex count per instance, instance count, start vertex location, and start instance location
// Use use max size of 4 unit for allocation
passData.output.dispatchIndirectBuffer = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(m_MaxViewCount * LightDefinitions.s_NumFeatureVariants * 4, sizeof(uint), GraphicsBuffer.Target.IndirectArguments) { name = "DispatchIndirectBuffer" }));
}
// Big Tile buffer
if (passData.runBigTilePrepass)
{
var nrBigTilesX = (m_MaxCameraWidth + 63) / 64;
var nrBigTilesY = (m_MaxCameraHeight + 63) / 64;
var nrBigTiles = nrBigTilesX * nrBigTilesY * m_MaxViewCount;
passData.output.bigTileLightList = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(InternalLightCullingDefs.s_MaxNrBigTileLightsPlusOne * nrBigTiles / 2, sizeof(uint)) { name = "BigTiles" }));
if (passData.supportsVolumetric)
{
passData.output.bigTileVolumetricLightList = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(InternalLightCullingDefs.s_MaxNrBigTileLightsPlusOne * nrBigTiles / 2, sizeof(uint)) { name = "BigTiles For Volumetric" }));
}
}
// Cluster buffers
var nrClustersX = (m_MaxCameraWidth + LightDefinitions.s_TileSizeClustered - 1) / LightDefinitions.s_TileSizeClustered;
var nrClustersY = (m_MaxCameraHeight + LightDefinitions.s_TileSizeClustered - 1) / LightDefinitions.s_TileSizeClustered;
var nrClusterTiles = nrClustersX * nrClustersY * m_MaxViewCount;
passData.output.perVoxelOffset = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc((int)LightCategory.Count * (1 << k_Log2NumClusters) * nrClusterTiles, sizeof(uint)) { name = "PerVoxelOffset" }));
passData.output.perVoxelLightLists = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(NumLightIndicesPerClusteredTile() * nrClusterTiles, sizeof(uint)) { name = "PerVoxelLightList" }));
if (tileAndClusterData.clusterNeedsDepth)
{
passData.output.perTileLogBaseTweak = builder.WriteBuffer(
renderGraph.CreateBuffer(new BufferDesc(nrClusterTiles, sizeof(float)) { name = "PerTileLogBaseTweak" }));
}
}
BuildGPULightListOutput BuildGPULightList(
RenderGraph renderGraph,
HDCamera hdCamera,
TileAndClusterData tileAndClusterData,
int totalLightCount,
ref ShaderVariablesLightList constantBuffer,
TextureHandle depthStencilBuffer,
TextureHandle stencilBufferCopy,
GBufferOutput gBuffer)
{
using (var builder = renderGraph.AddRenderPass<BuildGPULightListPassData>("Build Light List", out var passData, ProfilingSampler.Get(HDProfileId.BuildLightList)))
{
builder.EnableAsyncCompute(hdCamera.frameSettings.BuildLightListRunsAsync());
PrepareBuildGPULightListPassData(renderGraph, builder, hdCamera, tileAndClusterData, ref constantBuffer, totalLightCount, depthStencilBuffer, stencilBufferCopy, gBuffer, passData);
builder.SetRenderFunc(
(BuildGPULightListPassData data, RenderGraphContext context) =>
{
bool tileFlagsWritten = false;
ClearLightLists(data, context.cmd);
GenerateLightsScreenSpaceAABBs(data, context.cmd);
BigTilePrepass(data, context.cmd);
BuildPerTileLightList(data, ref tileFlagsWritten, context.cmd);
VoxelLightListGeneration(data, context.cmd);
BuildDispatchIndirectArguments(data, tileFlagsWritten, context.cmd);
});
return passData.output;
}
}
class PushGlobalCameraParamPassData
{
public ShaderVariablesGlobal globalCB;
public ShaderVariablesXR xrCB;
}
void PushGlobalCameraParams(RenderGraph renderGraph, HDCamera hdCamera)
{
using (var builder = renderGraph.AddRenderPass<PushGlobalCameraParamPassData>("Push Global Camera Parameters", out var passData))
{
passData.globalCB = m_ShaderVariablesGlobalCB;
passData.xrCB = m_ShaderVariablesXRCB;
builder.SetRenderFunc(
(PushGlobalCameraParamPassData data, RenderGraphContext context) =>
{
ConstantBuffer.PushGlobal(context.cmd, data.globalCB, HDShaderIDs._ShaderVariablesGlobal);
ConstantBuffer.PushGlobal(context.cmd, data.xrCB, HDShaderIDs._ShaderVariablesXR);
});
}
}
internal ShadowResult RenderShadows(RenderGraph renderGraph, HDCamera hdCamera, CullingResults cullResults, ref ShadowResult result)
{
m_ShadowManager.RenderShadows(m_RenderGraph, m_ShaderVariablesGlobalCB, hdCamera, cullResults, ref result);
// Need to restore global camera parameters.
PushGlobalCameraParams(renderGraph, hdCamera);
return result;
}
TextureHandle CreateDiffuseLightingBuffer(RenderGraph renderGraph, MSAASamples msaaSamples)
{
bool msaa = msaaSamples != MSAASamples.None;
return renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{
format = GraphicsFormat.B10G11R11_UFloatPack32,
enableRandomWrite = !msaa,
bindTextureMS = msaa,
msaaSamples = msaaSamples,
clearBuffer = true,
clearColor = Color.clear,
name = msaa ? "CameraSSSDiffuseLightingMSAA" : "CameraSSSDiffuseLighting"
});
}
class DeferredLightingPassData
{
public int numTilesX;
public int numTilesY;
public int numTiles;
public bool outputSplitLighting;
public bool enableFeatureVariants;
public bool enableShadowMasks;
public int numVariants;
public DebugDisplaySettings debugDisplaySettings;
// Compute Lighting
public ComputeShader deferredComputeShader;
public int viewCount;
public TextureHandle colorBuffer;
public TextureHandle sssDiffuseLightingBuffer;
public TextureHandle depthBuffer;
public TextureHandle depthTexture;
public int gbufferCount;
public int lightLayersTextureIndex;
public int shadowMaskTextureIndex;
public TextureHandle[] gbuffer = new TextureHandle[8];
public BufferHandle lightListBuffer;
public BufferHandle tileFeatureFlagsBuffer;
public BufferHandle tileListBuffer;
public BufferHandle dispatchIndirectBuffer;
public LightingBuffers lightingBuffers;
}
struct LightingOutput
{
public TextureHandle colorBuffer;
}
static void RenderComputeDeferredLighting(DeferredLightingPassData data, RenderTargetIdentifier[] colorBuffers, CommandBuffer cmd)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.RenderDeferredLightingCompute)))
{
data.deferredComputeShader.shaderKeywords = null;
switch (HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams.punctualShadowFilteringQuality)
{
case HDShadowFilteringQuality.Low:
data.deferredComputeShader.EnableKeyword("PUNCTUAL_SHADOW_LOW");
break;
case HDShadowFilteringQuality.Medium:
data.deferredComputeShader.EnableKeyword("PUNCTUAL_SHADOW_MEDIUM");
break;
case HDShadowFilteringQuality.High:
data.deferredComputeShader.EnableKeyword("PUNCTUAL_SHADOW_HIGH");
break;
default:
data.deferredComputeShader.EnableKeyword("PUNCTUAL_SHADOW_MEDIUM");
break;
}
switch (HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams.directionalShadowFilteringQuality)
{
case HDShadowFilteringQuality.Low:
data.deferredComputeShader.EnableKeyword("DIRECTIONAL_SHADOW_LOW");
break;
case HDShadowFilteringQuality.Medium:
data.deferredComputeShader.EnableKeyword("DIRECTIONAL_SHADOW_MEDIUM");
break;
case HDShadowFilteringQuality.High:
data.deferredComputeShader.EnableKeyword("DIRECTIONAL_SHADOW_HIGH");
break;
default:
data.deferredComputeShader.EnableKeyword("DIRECTIONAL_SHADOW_MEDIUM");
break;
}
switch (HDRenderPipeline.currentAsset.currentPlatformRenderPipelineSettings.hdShadowInitParams.areaShadowFilteringQuality)
{
case HDAreaShadowFilteringQuality.Medium:
data.deferredComputeShader.EnableKeyword("AREA_SHADOW_MEDIUM");
break;
case HDAreaShadowFilteringQuality.High:
data.deferredComputeShader.EnableKeyword("AREA_SHADOW_HIGH");
break;
default:
data.deferredComputeShader.EnableKeyword("AREA_SHADOW_MEDIUM");
break;
}
if (data.enableShadowMasks)
{
data.deferredComputeShader.EnableKeyword("SHADOWS_SHADOWMASK");
}
if (data.enableFeatureVariants)
{
for (int variant = 0; variant < data.numVariants; variant++)
{
var kernel = s_shadeOpaqueIndirectFptlKernels[variant];
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs._CameraDepthTexture, data.depthTexture);
// TODO: Is it possible to setup this outside the loop ? Can figure out how, get this: Property (specularLightingUAV) at kernel index (21) is not set
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs.specularLightingUAV, colorBuffers[0]);
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs.diffuseLightingUAV, colorBuffers[1]);
cmd.SetComputeBufferParam(data.deferredComputeShader, kernel, HDShaderIDs.g_vLightListTile, data.lightListBuffer);
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
// always do deferred lighting in blocks of 16x16 (not same as tiled light size)
cmd.SetComputeBufferParam(data.deferredComputeShader, kernel, HDShaderIDs.g_TileFeatureFlags, data.tileFeatureFlagsBuffer);
cmd.SetComputeIntParam(data.deferredComputeShader, HDShaderIDs.g_TileListOffset, variant * data.numTiles * data.viewCount);
cmd.SetComputeBufferParam(data.deferredComputeShader, kernel, HDShaderIDs.g_TileList, data.tileListBuffer);
cmd.DispatchCompute(data.deferredComputeShader, kernel, data.dispatchIndirectBuffer, (uint)variant * 3 * sizeof(uint));
}
}
else
{
var kernel = data.debugDisplaySettings.IsDebugDisplayEnabled() ? s_shadeOpaqueDirectFptlDebugDisplayKernel : s_shadeOpaqueDirectFptlKernel;
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs._CameraDepthTexture, data.depthTexture);
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs.specularLightingUAV, colorBuffers[0]);
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs.diffuseLightingUAV, colorBuffers[1]);
cmd.SetComputeBufferParam(data.deferredComputeShader, kernel, HDShaderIDs.g_vLightListTile, data.lightListBuffer);
cmd.SetComputeTextureParam(data.deferredComputeShader, kernel, HDShaderIDs._StencilTexture, data.depthBuffer, 0, RenderTextureSubElement.Stencil);
// 4x 8x8 groups per a 16x16 tile.
cmd.DispatchCompute(data.deferredComputeShader, kernel, data.numTilesX * 2, data.numTilesY * 2, data.viewCount);
}
}
}
LightingOutput RenderDeferredLighting(
RenderGraph renderGraph,
HDCamera hdCamera,
TextureHandle colorBuffer,
TextureHandle depthStencilBuffer,
TextureHandle depthPyramidTexture,
in LightingBuffers lightingBuffers,
in GBufferOutput gbuffer,
in ShadowResult shadowResult,
in BuildGPULightListOutput lightLists)
{
if (hdCamera.frameSettings.litShaderMode != LitShaderMode.Deferred ||
!hdCamera.frameSettings.IsEnabled(FrameSettingsField.OpaqueObjects))
return new LightingOutput();
using (var builder = renderGraph.AddRenderPass<DeferredLightingPassData>("Deferred Lighting", out var passData))
{
bool debugDisplayOrSceneLightOff = CoreUtils.IsSceneLightingDisabled(hdCamera.camera) || m_CurrentDebugDisplaySettings.IsDebugDisplayEnabled();
int w = hdCamera.actualWidth;
int h = hdCamera.actualHeight;
passData.numTilesX = (w + 15) / 16;
passData.numTilesY = (h + 15) / 16;
passData.numTiles = passData.numTilesX * passData.numTilesY;
passData.outputSplitLighting = hdCamera.frameSettings.IsEnabled(FrameSettingsField.SubsurfaceScattering);
passData.enableFeatureVariants = GetFeatureVariantsEnabled(hdCamera.frameSettings) && !debugDisplayOrSceneLightOff;
passData.enableShadowMasks = m_EnableBakeShadowMask;
passData.numVariants = LightDefinitions.s_NumFeatureVariants;
passData.debugDisplaySettings = m_CurrentDebugDisplaySettings;
// Compute Lighting
passData.deferredComputeShader = deferredComputeShader;
passData.viewCount = hdCamera.viewCount;
passData.colorBuffer = builder.WriteTexture(colorBuffer);
if (passData.outputSplitLighting)
{
passData.sssDiffuseLightingBuffer = builder.WriteTexture(lightingBuffers.diffuseLightingBuffer);
}
else
{
// TODO RENDERGRAPH: Check how to avoid this kind of pattern.
// Unfortunately, the low level needs this texture to always be bound with UAV enabled, so in order to avoid effectively creating the full resolution texture here,
// we need to create a small dummy texture.
passData.sssDiffuseLightingBuffer = builder.CreateTransientTexture(new TextureDesc(1, 1, true, true) { format = GraphicsFormat.B10G11R11_UFloatPack32, enableRandomWrite = true });
}
passData.depthBuffer = builder.ReadTexture(depthStencilBuffer);
passData.depthTexture = builder.ReadTexture(depthPyramidTexture);
passData.lightingBuffers = ReadLightingBuffers(lightingBuffers, builder);
passData.lightLayersTextureIndex = gbuffer.lightLayersTextureIndex;
passData.shadowMaskTextureIndex = gbuffer.shadowMaskTextureIndex;
passData.gbufferCount = gbuffer.gBufferCount;
for (int i = 0; i < gbuffer.gBufferCount; ++i)
passData.gbuffer[i] = builder.ReadTexture(gbuffer.mrt[i]);
HDShadowManager.ReadShadowResult(shadowResult, builder);
passData.lightListBuffer = builder.ReadBuffer(lightLists.lightList);
passData.tileFeatureFlagsBuffer = builder.ReadBuffer(lightLists.tileFeatureFlags);
passData.tileListBuffer = builder.ReadBuffer(lightLists.tileList);
passData.dispatchIndirectBuffer = builder.ReadBuffer(lightLists.dispatchIndirectBuffer);
var output = new LightingOutput();
output.colorBuffer = passData.colorBuffer;
builder.SetRenderFunc(
(DeferredLightingPassData data, RenderGraphContext context) =>
{
var colorBuffers = context.renderGraphPool.GetTempArray<RenderTargetIdentifier>(2);
colorBuffers[0] = data.colorBuffer;
colorBuffers[1] = data.sssDiffuseLightingBuffer;
// TODO RENDERGRAPH: Remove these SetGlobal and properly send these textures to the deferred passes and bind them directly to compute shaders.
// This can wait that we remove the old code path.
for (int i = 0; i < data.gbufferCount; ++i)
context.cmd.SetGlobalTexture(HDShaderIDs._GBufferTexture[i], data.gbuffer[i]);
if (data.lightLayersTextureIndex != -1)
context.cmd.SetGlobalTexture(HDShaderIDs._RenderingLayersTexture, data.gbuffer[data.lightLayersTextureIndex]);
else
context.cmd.SetGlobalTexture(HDShaderIDs._RenderingLayersTexture, TextureXR.GetWhiteTexture());
if (data.shadowMaskTextureIndex != -1)
context.cmd.SetGlobalTexture(HDShaderIDs._ShadowMaskTexture, data.gbuffer[data.shadowMaskTextureIndex]);
else
context.cmd.SetGlobalTexture(HDShaderIDs._ShadowMaskTexture, TextureXR.GetWhiteTexture());
BindGlobalLightingBuffers(data.lightingBuffers, context.cmd);
RenderComputeDeferredLighting(data, colorBuffers, context.cmd);
});
return output;
}
}
class RenderSSRPassData
{
public ComputeShader ssrCS;
public int tracingKernel;
public int reprojectionKernel;
public int accumulateNoWorldSpeedRejectionBothKernel;
public int accumulateNoWorldSpeedRejectionSurfaceKernel;
public int accumulateNoWorldSpeedRejectionHitKernel;
public int accumulateHardThresholdSpeedRejectionBothKernel;
public int accumulateHardThresholdSpeedRejectionSurfaceKernel;
public int accumulateHardThresholdSpeedRejectionHitKernel;
public int accumulateSmoothSpeedRejectionBothKernel;
public int accumulateSmoothSpeedRejectionSurfaceKernel;
public int accumulateSmoothSpeedRejectionHitKernel;
public int accumulateNoWorldSpeedRejectionBothDebugKernel;
public int accumulateNoWorldSpeedRejectionSurfaceDebugKernel;
public int accumulateNoWorldSpeedRejectionHitDebugKernel;
public int accumulateHardThresholdSpeedRejectionBothDebugKernel;
public int accumulateHardThresholdSpeedRejectionSurfaceDebugKernel;
public int accumulateHardThresholdSpeedRejectionHitDebugKernel;
public int accumulateSmoothSpeedRejectionBothDebugKernel;
public int accumulateSmoothSpeedRejectionSurfaceDebugKernel;
public int accumulateSmoothSpeedRejectionHitDebugKernel;
public bool transparentSSR;
public bool usePBRAlgo;
public bool previousAccumNeedClear;
public bool validColorPyramid;
public int width, height, viewCount;
public ComputeBuffer offsetBufferData;
public ShaderVariablesScreenSpaceReflection cb;
public TextureHandle depthBuffer;
public TextureHandle depthPyramid;
public TextureHandle normalBuffer;
public TextureHandle motionVectorsBuffer;
public TextureHandle colorPyramid;
public TextureHandle stencilBuffer;
public TextureHandle hitPointsTexture;
public TextureHandle ssrAccum;
public TextureHandle ssrAccumPrev;
public TextureHandle clearCoatMask;
public BufferHandle coarseStencilBuffer;
public BlueNoise blueNoise;
public HDCamera hdCamera;
public ComputeShader clearBuffer2DCS;
public int clearBuffer2DKernel;
public bool useAsync;
public float frameIndex;
public float roughnessBiasFactor;
public float speedRejection;
public float speedRejectionFactor;
public bool debugDisplaySpeed;
public bool enableWorldSmoothRejection;
public bool smoothSpeedRejection;
public bool motionVectorFromSurface;
public bool motionVectorFromHit;
}
static void ClearColorBuffer2D(RenderSSRPassData data, CommandBuffer cmd, TextureHandle rt, Color clearColor, bool async)
{
if (!async)
{
CoreUtils.SetRenderTarget(cmd, rt, ClearFlag.Color, clearColor);
}
else
{
cmd.SetComputeTextureParam(data.clearBuffer2DCS, data.clearBuffer2DKernel, HDShaderIDs._Buffer2D, rt);
cmd.SetComputeVectorParam(data.clearBuffer2DCS, HDShaderIDs._ClearValue, clearColor);
cmd.SetComputeVectorParam(data.clearBuffer2DCS, HDShaderIDs._BufferSize, new Vector4((float)data.width, (float)data.height, 0.0f, 0.0f));
cmd.DispatchCompute(data.clearBuffer2DCS, data.clearBuffer2DKernel, HDUtils.DivRoundUp(data.width, 8), HDUtils.DivRoundUp(data.height, 8), data.viewCount);
}
}
void UpdateSSRConstantBuffer(HDCamera hdCamera, ScreenSpaceReflection settings, bool isTransparent, ref ShaderVariablesScreenSpaceReflection cb)
{
float n = hdCamera.camera.nearClipPlane;
float f = hdCamera.camera.farClipPlane;
float thickness = settings.depthBufferThickness.value;
cb._SsrThicknessScale = 1.0f / (1.0f + thickness);
cb._SsrThicknessBias = -n / (f - n) * (thickness * cb._SsrThicknessScale);
cb._SsrIterLimit = settings.rayMaxIterations;
// We disable sky reflection for transparent in case of a scenario where a transparent object seeing the sky through it is visible in the reflection.
// As it has no depth it will appear extremely distorted (depth at infinity). This scenario happen frequently when you have transparent objects above water.
// Note that the sky is still visible, it just takes its value from reflection probe/skybox rather than on screen.
cb._SsrReflectsSky = isTransparent ? 0 : (settings.reflectSky.value ? 1 : 0);
cb._SsrStencilBit = (int)StencilUsage.TraceReflectionRay;
float roughnessFadeStart = 1 - settings.smoothnessFadeStart;
cb._SsrRoughnessFadeEnd = 1 - settings.minSmoothness;
float roughnessFadeLength = cb._SsrRoughnessFadeEnd - roughnessFadeStart;
cb._SsrRoughnessFadeEndTimesRcpLength = (roughnessFadeLength != 0) ? (cb._SsrRoughnessFadeEnd * (1.0f / roughnessFadeLength)) : 1;
cb._SsrRoughnessFadeRcpLength = (roughnessFadeLength != 0) ? (1.0f / roughnessFadeLength) : 0;
cb._SsrEdgeFadeRcpLength = Mathf.Min(1.0f / settings.screenFadeDistance.value, float.MaxValue);
cb._SsrColorPyramidMaxMip = hdCamera.colorPyramidHistoryMipCount - 1;
cb._SsrDepthPyramidMaxMip = hdCamera.depthBufferMipChainInfo.mipLevelCount - 1;
if (hdCamera.isFirstFrame || hdCamera.cameraFrameCount <= 3)
{
cb._SsrAccumulationAmount = 1.0f;
}
else
{
cb._SsrAccumulationAmount = Mathf.Pow(2, Mathf.Lerp(0.0f, -7.0f, settings.accumulationFactor.value));
}
if (settings.enableWorldSpeedRejection.value && !settings.speedSmoothReject.value)
cb._SsrPBRSpeedRejection = Mathf.Clamp01(1.0f - settings.speedRejectionParam.value);
else
cb._SsrPBRSpeedRejection = Mathf.Clamp01(settings.speedRejectionParam.value);
cb._SsrPBRBias = settings.biasFactor.value;
cb._SsrPRBSpeedRejectionScalerFactor = Mathf.Pow(settings.speedRejectionScalerFactor.value * 0.1f, 2.0f);
}
TextureHandle RenderSSR(RenderGraph renderGraph,
HDCamera hdCamera,
ref PrepassOutput prepassOutput,
TextureHandle clearCoatMask,
TextureHandle rayCountTexture,
TextureHandle historyValidationTexture,
Texture skyTexture,
bool transparent)
{
if (!hdCamera.IsSSREnabled(transparent))
return renderGraph.defaultResources.blackTextureXR;
TextureHandle result;
var settings = hdCamera.volumeStack.GetComponent<ScreenSpaceReflection>();
if (EnableRayTracedReflections(hdCamera, settings))
{
result = RenderRayTracedReflections(renderGraph, hdCamera,
prepassOutput, clearCoatMask, skyTexture, rayCountTexture, historyValidationTexture,
m_ShaderVariablesRayTracingCB, transparent);
}
else
{
if (transparent)
{
// NOTE: Currently we profiled that generating the HTile for SSR and using it is not worth it the optimization.
// However if the generated HTile will be used for something else but SSR, this should be made NOT resolve only and
// re-enabled in the shader.
BuildCoarseStencilAndResolveIfNeeded(renderGraph, hdCamera, resolveOnly: true, ref prepassOutput);
}
// The first color pyramid of the frame is generated after the SSR transparent, so we have no choice but to use the previous
// frame color pyramid (that includes transparents from the previous frame).
RTHandle colorPyramidRT = hdCamera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.ColorBufferMipChain);
if (colorPyramidRT == null)
return renderGraph.defaultResources.blackTextureXR;
using (var builder = renderGraph.AddRenderPass<RenderSSRPassData>("Render SSR", out var passData))
{
// We disable async for transparent SSR as it would cause direct sync to the graphics pipe and would compete with other heavy passes for GPU resource.
bool useAsync = hdCamera.frameSettings.SSRRunsAsync() && !transparent;
builder.EnableAsyncCompute(useAsync);
bool usePBRAlgo = !transparent && settings.usedAlgorithm.value == ScreenSpaceReflectionAlgorithm.PBRAccumulation;
var colorPyramid = renderGraph.ImportTexture(colorPyramidRT);
var volumeSettings = hdCamera.volumeStack.GetComponent<ScreenSpaceReflection>();
if (usePBRAlgo)
hdCamera.AllocateScreenSpaceAccumulationHistoryBuffer(1.0f);
UpdateSSRConstantBuffer(hdCamera, volumeSettings, transparent, ref passData.cb);
passData.hdCamera = hdCamera;
passData.blueNoise = GetBlueNoiseManager();
passData.ssrCS = m_ScreenSpaceReflectionsCS;
passData.tracingKernel = m_SsrTracingKernel;
passData.reprojectionKernel = m_SsrReprojectionKernel;
passData.accumulateNoWorldSpeedRejectionBothKernel = m_SsrAccumulateNoWorldSpeedRejectionBothKernel;
passData.accumulateNoWorldSpeedRejectionSurfaceKernel = m_SsrAccumulateNoWorldSpeedRejectionSurfaceKernel;
passData.accumulateNoWorldSpeedRejectionHitKernel = m_SsrAccumulateNoWorldSpeedRejectionHitKernel;
passData.accumulateHardThresholdSpeedRejectionBothKernel = m_SsrAccumulateHardThresholdSpeedRejectionBothKernel;
passData.accumulateHardThresholdSpeedRejectionSurfaceKernel = m_SsrAccumulateHardThresholdSpeedRejectionSurfaceKernel;
passData.accumulateHardThresholdSpeedRejectionHitKernel = m_SsrAccumulateHardThresholdSpeedRejectionHitKernel;
passData.accumulateSmoothSpeedRejectionBothKernel = m_SsrAccumulateSmoothSpeedRejectionBothKernel;
passData.accumulateSmoothSpeedRejectionSurfaceKernel = m_SsrAccumulateSmoothSpeedRejectionSurfaceKernel;
passData.accumulateSmoothSpeedRejectionHitKernel = m_SsrAccumulateSmoothSpeedRejectionHitKernel;
passData.accumulateNoWorldSpeedRejectionBothDebugKernel = m_SsrAccumulateNoWorldSpeedRejectionBothDebugKernel;
passData.accumulateNoWorldSpeedRejectionSurfaceDebugKernel = m_SsrAccumulateNoWorldSpeedRejectionSurfaceDebugKernel;
passData.accumulateNoWorldSpeedRejectionHitDebugKernel = m_SsrAccumulateNoWorldSpeedRejectionHitDebugKernel;
passData.accumulateHardThresholdSpeedRejectionBothDebugKernel = m_SsrAccumulateHardThresholdSpeedRejectionBothDebugKernel;
passData.accumulateHardThresholdSpeedRejectionSurfaceDebugKernel = m_SsrAccumulateHardThresholdSpeedRejectionSurfaceDebugKernel;
passData.accumulateHardThresholdSpeedRejectionHitDebugKernel = m_SsrAccumulateHardThresholdSpeedRejectionHitDebugKernel;
passData.accumulateSmoothSpeedRejectionBothDebugKernel = m_SsrAccumulateSmoothSpeedRejectionBothDebugKernel;
passData.accumulateSmoothSpeedRejectionSurfaceDebugKernel = m_SsrAccumulateSmoothSpeedRejectionSurfaceDebugKernel;
passData.accumulateSmoothSpeedRejectionHitDebugKernel = m_SsrAccumulateSmoothSpeedRejectionHitDebugKernel;
passData.transparentSSR = transparent;
passData.usePBRAlgo = usePBRAlgo;
passData.width = hdCamera.actualWidth;
passData.height = hdCamera.actualHeight;
passData.viewCount = hdCamera.viewCount;
passData.offsetBufferData = hdCamera.depthBufferMipChainInfo.GetOffsetBufferData(m_DepthPyramidMipLevelOffsetsBuffer);
passData.previousAccumNeedClear = usePBRAlgo && (hdCamera.isFirstFrame || hdCamera.resetPostProcessingHistory);
hdCamera.currentSSRAlgorithm = volumeSettings.usedAlgorithm.value; // Store for next frame comparison
passData.validColorPyramid = hdCamera.colorPyramidHistoryValidFrames > 1;
passData.depthBuffer = builder.ReadTexture(prepassOutput.depthBuffer);
passData.depthPyramid = builder.ReadTexture(prepassOutput.depthPyramidTexture);
passData.colorPyramid = builder.ReadTexture(colorPyramid);
passData.stencilBuffer = builder.ReadTexture(prepassOutput.stencilBuffer);
passData.clearCoatMask = builder.ReadTexture(clearCoatMask);
//passData.coarseStencilBuffer = builder.ReadBuffer(prepassOutput.coarseStencilBuffer);
passData.normalBuffer = builder.ReadTexture(prepassOutput.resolvedNormalBuffer);
passData.motionVectorsBuffer = builder.ReadTexture(prepassOutput.resolvedMotionVectorsBuffer);
if (hdCamera.isFirstFrame || hdCamera.cameraFrameCount <= 2)
{
passData.frameIndex = 1.0f;
}
else
{
passData.frameIndex = ((float)hdCamera.cameraFrameCount);
}
passData.roughnessBiasFactor = volumeSettings.biasFactor.value;
passData.debugDisplaySpeed = m_CurrentDebugDisplaySettings.data.fullScreenDebugMode == FullScreenDebugMode.ScreenSpaceReflectionSpeedRejection;
passData.speedRejection = volumeSettings.speedRejectionParam.value;
passData.speedRejectionFactor = volumeSettings.speedRejectionScalerFactor.value;
passData.enableWorldSmoothRejection = volumeSettings.enableWorldSpeedRejection.value;
passData.smoothSpeedRejection = volumeSettings.speedSmoothReject.value;
passData.motionVectorFromSurface = volumeSettings.speedSurfaceOnly.value;
passData.motionVectorFromHit = volumeSettings.speedTargetOnly.value;
passData.clearBuffer2DCS = m_ClearBuffer2DCS;
passData.clearBuffer2DKernel = m_ClearBuffer2DKernel;
passData.useAsync = useAsync;
// In practice, these textures are sparse (mostly black). Therefore, clearing them is fast (due to CMASK),
// and much faster than fully overwriting them from within SSR shaders.
passData.hitPointsTexture = builder.CreateTransientTexture(new TextureDesc(Vector2.one, true, true)
{ format = GraphicsFormat.R16G16_UNorm, clearBuffer = !useAsync, clearColor = Color.clear, enableRandomWrite = true, name = transparent ? "SSR_Hit_Point_Texture_Trans" : "SSR_Hit_Point_Texture" });
if (usePBRAlgo)
{
passData.ssrAccum = builder.WriteTexture(renderGraph.ImportTexture(hdCamera.GetCurrentFrameRT((int)HDCameraFrameHistoryType.ScreenSpaceReflectionAccumulation)));
passData.ssrAccumPrev = builder.WriteTexture(renderGraph.ImportTexture(hdCamera.GetPreviousFrameRT((int)HDCameraFrameHistoryType.ScreenSpaceReflectionAccumulation)));
}
else
{
passData.ssrAccum = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ format = GraphicsFormat.R16G16B16A16_SFloat, clearBuffer = !useAsync, clearColor = Color.clear, enableRandomWrite = true, name = "SSR_Lighting_Texture" }));
}
builder.SetRenderFunc(
(RenderSSRPassData data, RenderGraphContext ctx) =>
{
var cs = data.ssrCS;
ConstantBuffer.Push(ctx.cmd, data.cb, cs, HDShaderIDs._ShaderVariablesScreenSpaceReflection);
BlueNoise.BindDitheredTextureSet(ctx.cmd, data.blueNoise.DitheredTextureSet1SPP());
CoreUtils.SetKeyword(ctx.cmd, "SSR_APPROX", !data.usePBRAlgo);
CoreUtils.SetKeyword(ctx.cmd, "DEPTH_SOURCE_NOT_FROM_MIP_CHAIN", data.transparentSSR);
if (data.usePBRAlgo || data.useAsync)
{
// When non pbr and not async, clear is done when the accumulation texture is created
ClearColorBuffer2D(data, ctx.cmd, data.ssrAccum, Color.clear, data.useAsync);
}
if (data.usePBRAlgo && (data.previousAccumNeedClear || data.debugDisplaySpeed))
{
ClearColorBuffer2D(data, ctx.cmd, data.ssrAccumPrev, Color.clear, data.useAsync);
}
if (data.useAsync)
{
ClearColorBuffer2D(data, ctx.cmd, data.hitPointsTexture, Color.clear, data.useAsync);
}
using (new ProfilingScope(ctx.cmd, ProfilingSampler.Get(HDProfileId.SsrTracing)))
{
// cmd.SetComputeTextureParam(cs, kernel, "_SsrDebugTexture", m_SsrDebugTexture);
// Bind the non mip chain if we are rendering the transparent version
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._CameraDepthTexture, data.depthPyramid);
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._NormalBufferTexture, data.normalBuffer);
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._SsrClearCoatMaskTexture, data.clearCoatMask);
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._SsrHitPointTexture, data.hitPointsTexture);
RTHandle stencilBuffer = data.stencilBuffer;
if (stencilBuffer.rt.stencilFormat == GraphicsFormat.None) // We are accessing MSAA resolved version and not the depth stencil buffer directly.
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._StencilTexture, stencilBuffer);
else
ctx.cmd.SetComputeTextureParam(cs, data.tracingKernel, HDShaderIDs._StencilTexture, stencilBuffer, 0, RenderTextureSubElement.Stencil);
//ctx.cmd.SetComputeBufferParam(cs, data.tracingKernel, HDShaderIDs._CoarseStencilBuffer, data.coarseStencilBuffer);
ctx.cmd.SetComputeBufferParam(cs, data.tracingKernel, HDShaderIDs._DepthPyramidMipLevelOffsets, data.offsetBufferData);
ctx.cmd.DispatchCompute(cs, data.tracingKernel, HDUtils.DivRoundUp(data.width, 8), HDUtils.DivRoundUp(data.height, 8), data.viewCount);
}
using (new ProfilingScope(ctx.cmd, ProfilingSampler.Get(HDProfileId.SsrReprojection)))
{
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._CameraDepthTexture, data.depthPyramid);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._ColorPyramidTexture, data.colorPyramid);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._NormalBufferTexture, data.normalBuffer);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._SsrHitPointTexture, data.hitPointsTexture);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._SSRAccumTexture, data.ssrAccum);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._SsrClearCoatMaskTexture, data.clearCoatMask);
ctx.cmd.SetComputeTextureParam(cs, data.reprojectionKernel, HDShaderIDs._CameraMotionVectorsTexture, data.motionVectorsBuffer);
ctx.cmd.DispatchCompute(cs, data.reprojectionKernel, HDUtils.DivRoundUp(data.width, 8), HDUtils.DivRoundUp(data.height, 8), data.viewCount);
}
if (data.usePBRAlgo)
{
if (!data.validColorPyramid)
{
ClearColorBuffer2D(data, ctx.cmd, data.ssrAccum, Color.clear, data.useAsync);
ClearColorBuffer2D(data, ctx.cmd, data.ssrAccumPrev, Color.clear, data.useAsync);
}
else
{
using (new ProfilingScope(ctx.cmd, ProfilingSampler.Get(HDProfileId.SsrAccumulate)))
{
int pass;
if (data.debugDisplaySpeed)
{
if (!data.enableWorldSmoothRejection)
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateNoWorldSpeedRejectionBothDebugKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateNoWorldSpeedRejectionHitDebugKernel;
else
pass = data.accumulateNoWorldSpeedRejectionSurfaceDebugKernel;
}
else
{
if (data.smoothSpeedRejection)
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateSmoothSpeedRejectionBothDebugKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateSmoothSpeedRejectionHitDebugKernel;
else
pass = data.accumulateSmoothSpeedRejectionSurfaceDebugKernel;
}
else
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateHardThresholdSpeedRejectionBothDebugKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateHardThresholdSpeedRejectionHitDebugKernel;
else
pass = data.accumulateHardThresholdSpeedRejectionSurfaceDebugKernel;
}
}
}
else
{
if (!data.enableWorldSmoothRejection)
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateNoWorldSpeedRejectionBothKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateNoWorldSpeedRejectionHitKernel;
else
pass = data.accumulateNoWorldSpeedRejectionSurfaceKernel;
}
else
{
if (data.smoothSpeedRejection)
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateSmoothSpeedRejectionBothKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateSmoothSpeedRejectionHitKernel;
else
pass = data.accumulateSmoothSpeedRejectionSurfaceKernel;
}
else
{
if (data.motionVectorFromSurface && data.motionVectorFromHit)
pass = data.accumulateHardThresholdSpeedRejectionBothKernel;
else if (data.motionVectorFromHit)
pass = data.accumulateHardThresholdSpeedRejectionHitKernel;
else
pass = data.accumulateHardThresholdSpeedRejectionSurfaceKernel;
}
}
}
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._DepthTexture, data.depthBuffer);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._CameraDepthTexture, data.depthPyramid);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._NormalBufferTexture, data.normalBuffer);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._ColorPyramidTexture, data.colorPyramid);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._SsrHitPointTexture, data.hitPointsTexture);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._SSRAccumTexture, data.ssrAccum);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._SsrAccumPrev, data.ssrAccumPrev);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._SsrClearCoatMaskTexture, data.clearCoatMask);
ctx.cmd.SetComputeTextureParam(cs, pass, HDShaderIDs._CameraMotionVectorsTexture, data.motionVectorsBuffer);
ctx.cmd.DispatchCompute(cs, pass, HDUtils.DivRoundUp(data.width, 8), HDUtils.DivRoundUp(data.height, 8), data.viewCount);
}
}
}
});
if (usePBRAlgo)
{
PushFullScreenDebugTexture(renderGraph, passData.ssrAccum, FullScreenDebugMode.ScreenSpaceReflectionsAccum);
PushFullScreenDebugTexture(renderGraph, passData.ssrAccumPrev, FullScreenDebugMode.ScreenSpaceReflectionsPrev);
}
PushFullScreenDebugTexture(renderGraph, passData.ssrAccum, FullScreenDebugMode.ScreenSpaceReflectionSpeedRejection);
result = passData.ssrAccum;
}
if (!hdCamera.colorPyramidHistoryIsValid)
{
result = renderGraph.defaultResources.blackTextureXR;
}
}
PushFullScreenDebugTexture(renderGraph, result, transparent ? FullScreenDebugMode.TransparentScreenSpaceReflections : FullScreenDebugMode.ScreenSpaceReflections);
return result;
}
class RenderContactShadowPassData
{
public ComputeShader contactShadowsCS;
public int kernel;
public Vector4 params1;
public Vector4 params2;
public Vector4 params3;
public int numTilesX;
public int numTilesY;
public int viewCount;
public bool rayTracingEnabled;
public RayTracingShader contactShadowsRTS;
public RayTracingAccelerationStructure accelerationStructure;
public int actualWidth;
public int actualHeight;
public int depthTextureParameterName;
public LightLoopLightData lightLoopLightData;
public TextureHandle depthTexture;
public TextureHandle contactShadowsTexture;
public BufferHandle lightList;
}
TextureHandle RenderContactShadows(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle depthTexture, in BuildGPULightListOutput lightLists, int firstMipOffsetY)
{
if (!WillRenderContactShadow())
return renderGraph.defaultResources.blackUIntTextureXR;
TextureHandle result;
using (var builder = renderGraph.AddRenderPass<RenderContactShadowPassData>("Contact Shadows", out var passData))
{
builder.EnableAsyncCompute(hdCamera.frameSettings.ContactShadowsRunsAsync());
// Avoid garbage when visualizing contact shadows.
bool clearBuffer = m_CurrentDebugDisplaySettings.data.fullScreenDebugMode == FullScreenDebugMode.ContactShadows;
bool msaa = hdCamera.msaaEnabled;
passData.contactShadowsCS = contactShadowComputeShader;
passData.contactShadowsCS.shaderKeywords = null;
if (msaa)
{
passData.contactShadowsCS.EnableKeyword("ENABLE_MSAA");
}
passData.rayTracingEnabled = RayTracedContactShadowsRequired() && GetRayTracingState();
if (hdCamera.frameSettings.IsEnabled(FrameSettingsField.RayTracing))
{
passData.contactShadowsRTS = rayTracingResources.contactShadowRayTracingRT;
passData.accelerationStructure = RequestAccelerationStructure(hdCamera);
passData.actualWidth = hdCamera.actualWidth;
passData.actualHeight = hdCamera.actualHeight;
}
passData.kernel = s_deferredContactShadowKernel;
float contactShadowRange = Mathf.Clamp(m_ContactShadows.fadeDistance.value, 0.0f, m_ContactShadows.maxDistance.value);
float contactShadowFadeEnd = m_ContactShadows.maxDistance.value;
float contactShadowOneOverFadeRange = 1.0f / Math.Max(1e-6f, contactShadowRange);
float contactShadowMinDist = Mathf.Min(m_ContactShadows.minDistance.value, contactShadowFadeEnd);
float contactShadowFadeIn = Mathf.Clamp(m_ContactShadows.fadeInDistance.value, 1e-6f, contactShadowFadeEnd);
passData.params1 = new Vector4(m_ContactShadows.length.value, m_ContactShadows.distanceScaleFactor.value, contactShadowFadeEnd, contactShadowOneOverFadeRange);
passData.params2 = new Vector4(firstMipOffsetY, contactShadowMinDist, contactShadowFadeIn, m_ContactShadows.rayBias.value * 0.01f);
passData.params3 = new Vector4(m_ContactShadows.sampleCount, m_ContactShadows.thicknessScale.value * 10.0f, 0.0f, 0.0f);
int deferredShadowTileSize = 8; // Must match ContactShadows.compute
passData.numTilesX = (hdCamera.actualWidth + (deferredShadowTileSize - 1)) / deferredShadowTileSize;
passData.numTilesY = (hdCamera.actualHeight + (deferredShadowTileSize - 1)) / deferredShadowTileSize;
passData.viewCount = hdCamera.viewCount;
passData.depthTextureParameterName = msaa ? HDShaderIDs._CameraDepthValuesTexture : HDShaderIDs._CameraDepthTexture;
passData.lightLoopLightData = m_LightLoopLightData;
passData.lightList = builder.ReadBuffer(lightLists.lightList);
passData.depthTexture = builder.ReadTexture(depthTexture);
passData.contactShadowsTexture = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ format = GraphicsFormat.R32_UInt, enableRandomWrite = true, clearBuffer = clearBuffer, clearColor = Color.clear, name = "ContactShadowsBuffer" }));
result = passData.contactShadowsTexture;
builder.SetRenderFunc(
(RenderContactShadowPassData data, RenderGraphContext ctx) =>
{
ctx.cmd.SetComputeVectorParam(data.contactShadowsCS, HDShaderIDs._ContactShadowParamsParameters, data.params1);
ctx.cmd.SetComputeVectorParam(data.contactShadowsCS, HDShaderIDs._ContactShadowParamsParameters2, data.params2);
ctx.cmd.SetComputeVectorParam(data.contactShadowsCS, HDShaderIDs._ContactShadowParamsParameters3, data.params3);
ctx.cmd.SetComputeBufferParam(data.contactShadowsCS, data.kernel, HDShaderIDs._DirectionalLightDatas, data.lightLoopLightData.directionalLightData);
// Send light list to the compute
ctx.cmd.SetComputeBufferParam(data.contactShadowsCS, data.kernel, HDShaderIDs._LightDatas, data.lightLoopLightData.lightData);
ctx.cmd.SetComputeBufferParam(data.contactShadowsCS, data.kernel, HDShaderIDs.g_vLightListTile, data.lightList);
ctx.cmd.SetComputeTextureParam(data.contactShadowsCS, data.kernel, data.depthTextureParameterName, data.depthTexture);
ctx.cmd.SetComputeTextureParam(data.contactShadowsCS, data.kernel, HDShaderIDs._ContactShadowTextureUAV, data.contactShadowsTexture);
ctx.cmd.DispatchCompute(data.contactShadowsCS, data.kernel, data.numTilesX, data.numTilesY, data.viewCount);
if (data.rayTracingEnabled)
{
ctx.cmd.SetRayTracingShaderPass(data.contactShadowsRTS, "VisibilityDXR");
ctx.cmd.SetRayTracingAccelerationStructure(data.contactShadowsRTS, HDShaderIDs._RaytracingAccelerationStructureName, data.accelerationStructure);
ctx.cmd.SetRayTracingVectorParam(data.contactShadowsRTS, HDShaderIDs._ContactShadowParamsParameters, data.params1);
ctx.cmd.SetRayTracingVectorParam(data.contactShadowsRTS, HDShaderIDs._ContactShadowParamsParameters2, data.params2);
ctx.cmd.SetRayTracingBufferParam(data.contactShadowsRTS, HDShaderIDs._DirectionalLightDatas, data.lightLoopLightData.directionalLightData);
// Send light list to the compute
ctx.cmd.SetRayTracingBufferParam(data.contactShadowsRTS, HDShaderIDs._LightDatas, data.lightLoopLightData.lightData);
ctx.cmd.SetRayTracingBufferParam(data.contactShadowsRTS, HDShaderIDs.g_vLightListTile, data.lightList);
ctx.cmd.SetRayTracingTextureParam(data.contactShadowsRTS, HDShaderIDs._DepthTexture, data.depthTexture);
ctx.cmd.SetRayTracingTextureParam(data.contactShadowsRTS, HDShaderIDs._ContactShadowTextureUAV, data.contactShadowsTexture);
ctx.cmd.DispatchRays(data.contactShadowsRTS, "RayGenContactShadows", (uint)data.actualWidth, (uint)data.actualHeight, (uint)data.viewCount);
}
});
}
PushFullScreenDebugTexture(renderGraph, result, FullScreenDebugMode.ContactShadows);
return result;
}
}
}