using System;
using System.Runtime.InteropServices;
using UnityEngine.Rendering.UnifiedRayTracing;
using Unity.Collections;
using UnityEngine.Rendering.Sampling;
namespace UnityEngine.Rendering
{
partial class AdaptiveProbeVolumes
{
///
/// Rendering Layer baker
///
abstract class RenderingLayerBaker : IDisposable
{
/// The current baking step.
public abstract ulong currentStep { get; }
/// The total amount of step.
public abstract ulong stepCount { get; }
/// Array storing the rendering layer mask per probe. Only the first 4 bits are used.
public abstract NativeArray renderingLayerMasks { get; }
///
/// This is called before the start of baking to allow allocating necessary resources.
///
/// The baking set that is currently baked.
/// The probe positions.
public abstract void Initialize(ProbeVolumeBakingSet bakingSet, NativeArray probePositions);
///
/// Run a baking step. Baking is considered done when currentStep property equals stepCount.
///
/// Return false if bake failed and should be stopped.
public abstract bool Step();
///
/// Performs necessary tasks to free allocated resources.
///
public abstract void Dispose();
}
class DefaultRenderingLayer : RenderingLayerBaker
{
const int k_MaxProbeCountPerBatch = 65535 * 64;
static readonly int _ProbePositions = Shader.PropertyToID("_ProbePositions");
static readonly int _LayerMasks = Shader.PropertyToID("_LayerMasks");
static readonly int _RenderingLayerMasks = Shader.PropertyToID("_RenderingLayerMasks");
static readonly int _SobolBuffer = Shader.PropertyToID("_SobolMatricesBuffer");
int batchIndex, batchCount;
Vector4 regionMasks;
// Input data
NativeArray probePositions;
// Output buffers
GraphicsBuffer layerMaskBuffer;
NativeArray layerMask;
public override NativeArray renderingLayerMasks => layerMask;
CommandBuffer cmd;
AccelStructAdapter m_AccelerationStructure;
GraphicsBuffer scratchBuffer;
GraphicsBuffer probePositionsBuffer;
GraphicsBuffer sobolBuffer;
public override ulong currentStep => (ulong)batchIndex;
public override ulong stepCount => (ulong)batchCount;
public override void Initialize(ProbeVolumeBakingSet bakingSet, NativeArray positions)
{
// Divide the job into batches to reduce memory usage.
batchCount = CoreUtils.DivRoundUp(bakingSet.useRenderingLayers ? positions.Length : 0, k_MaxProbeCountPerBatch);
batchIndex = 0;
probePositions = positions;
if (batchCount == 0)
return;
regionMasks = Vector4.zero;
for (int i = 0; i < bakingSet.renderingLayerMasks.Length; i++)
regionMasks[i] = Unity.Mathematics.math.asfloat(bakingSet.renderingLayerMasks[i].mask);
// Allocate array storing results
layerMask = new NativeArray(probePositions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
// Create acceleration structure
m_AccelerationStructure = BuildAccelerationStructure();
int batchSize = Mathf.Min(k_MaxProbeCountPerBatch, probePositions.Length);
probePositionsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf());
layerMaskBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf());
scratchBuffer = RayTracingHelper.CreateScratchBufferForBuildAndDispatch(m_AccelerationStructure.GetAccelerationStructure(), s_TracingContext.shaderRL, (uint)batchSize, 1, 1);
int sobolBufferSize = (int)(SobolData.SobolDims * SobolData.SobolSize);
sobolBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, sobolBufferSize, Marshal.SizeOf());
sobolBuffer.SetData(SobolData.SobolMatrices);
cmd = new CommandBuffer();
m_AccelerationStructure.Build(cmd, ref scratchBuffer);
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
static AccelStructAdapter BuildAccelerationStructure()
{
var accelStruct = s_TracingContext.CreateAccelerationStructure();
var contributors = m_BakingBatch.contributors;
foreach (var renderer in contributors.renderers)
{
var mesh = renderer.component.GetComponent().sharedMesh;
if (mesh == null)
continue;
int subMeshCount = mesh.subMeshCount;
var matIndices = new uint[subMeshCount];
Array.Fill(matIndices, renderer.component.renderingLayerMask); // repurpose the material id as we don't need it here
var perSubMeshMask = new uint[subMeshCount];
Array.Fill(perSubMeshMask, GetInstanceMask(renderer.component.shadowCastingMode));
accelStruct.AddInstance(renderer.component.GetInstanceID(), renderer.component, perSubMeshMask, matIndices, 1);
}
foreach (var terrain in contributors.terrains)
{
uint mask = GetInstanceMask(terrain.component.shadowCastingMode);
uint materialID = terrain.component.renderingLayerMask; // repurpose the material id as we don't need it here
accelStruct.AddInstance(terrain.component.GetInstanceID(), terrain.component, new uint[1] { mask }, new uint[1] { materialID }, 1);
}
return accelStruct;
}
public override bool Step()
{
if (currentStep >= stepCount)
return true;
var shader = s_TracingContext.shaderRL;
int batchOffset = batchIndex * k_MaxProbeCountPerBatch;
int batchSize = Mathf.Min(probePositions.Length - batchOffset, k_MaxProbeCountPerBatch);
cmd.SetBufferData(probePositionsBuffer, probePositions.GetSubArray(batchOffset, batchSize));
m_AccelerationStructure.Bind(cmd, "_AccelStruct", shader);
shader.SetVectorParam(cmd, _RenderingLayerMasks, regionMasks);
shader.SetBufferParam(cmd, _ProbePositions, probePositionsBuffer);
shader.SetBufferParam(cmd, _LayerMasks, layerMaskBuffer);
shader.SetBufferParam(cmd, _SobolBuffer, sobolBuffer);
shader.Dispatch(cmd, scratchBuffer, (uint)batchSize, 1, 1);
batchIndex++;
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
FetchResults(batchOffset, batchSize);
return true;
}
void FetchResults(int batchOffset, int batchSize)
{
var batchLayers = layerMask.GetSubArray(batchOffset, batchSize);
var req = AsyncGPUReadback.RequestIntoNativeArray(ref batchLayers, layerMaskBuffer, batchSize * sizeof(uint), 0);
// TODO: use double buffering to hide readback latency
req.WaitForCompletion();
}
public override void Dispose()
{
if (m_AccelerationStructure == null)
return;
cmd.Dispose();
scratchBuffer?.Dispose();
probePositionsBuffer.Dispose();
m_AccelerationStructure.Dispose();
sobolBuffer?.Dispose();
layerMaskBuffer.Dispose();
layerMask.Dispose();
}
}
}
}