#if HDRP_PRESENT
using Unity.Collections.LowLevel.Unsafe;
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering;
namespace UnityEngine.Perception.Sensors
{
///
/// CustomPass which computes object count for each label in the given LabelingConfiguration in the frame.
/// Requires the texture produced by an InstanceSegmentationPass.
///
public class ObjectCountPass : GroundTruthPass
{
const int k_StartingObjectCount = 1 << 8;
public RenderTexture SegmentationTexture;
public LabelingConfiguration LabelingConfiguration;
ComputeShader m_ComputeShader;
ComputeBuffer m_InstanceIdPresenceMask;
ComputeBuffer m_InstanceIdToClassId;
ComputeBuffer m_ClassCounts;
NativeList m_InstanceIdToClassIdLookup;
HashSet m_CamerasRendered = new HashSet();
bool m_IdBuffersNeedUpdating;
bool m_DidComputeLastFrame;
public ObjectCountPass(Camera camera) : base(camera)
{
}
// ReSharper disable once UnusedMember.Global
public ObjectCountPass() : base(null)
{
}
public bool WriteToLog { get; set; }
public override void SetupMaterialProperties(MaterialPropertyBlock mpb, MeshRenderer meshRenderer, Labeling labeling, uint instanceId)
{
if (!m_InstanceIdToClassIdLookup.IsCreated)
{
m_InstanceIdToClassIdLookup = new NativeList(k_StartingObjectCount, Allocator.Persistent);
}
if (LabelingConfiguration.TryGetMatchingConfigurationIndex(labeling, out var index))
{
if (m_InstanceIdToClassIdLookup.Length <= instanceId)
{
m_InstanceIdToClassIdLookup.Resize((int)instanceId + 1, NativeArrayOptions.ClearMemory);
}
m_IdBuffersNeedUpdating = true;
m_InstanceIdToClassIdLookup[(int) instanceId] = index + 1;
}
}
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
{
base.Setup(renderContext, cmd);
m_ComputeShader = Resources.Load("LabeledObjectHistogram");
var objectCount = k_StartingObjectCount;
UpdateIdBufferSizes(objectCount);
m_ClassCounts = new ComputeBuffer(LabelingConfiguration.LabelingConfigurations.Count + 1, UnsafeUtility.SizeOf(), ComputeBufferType.Structured);
m_DataLogger = new Unity.Simulation.Logger("LabelHistogram");
RenderPipelineManager.endCameraRendering += OnEndCameraRendering;
}
void OnEndCameraRendering(ScriptableRenderContext renderContext, Camera camera)
{
}
void UpdateIdBufferSizes(int objectCount)
{
var presenceMaskSizeNeeded = objectCount;
if (m_InstanceIdPresenceMask == null || presenceMaskSizeNeeded > m_InstanceIdPresenceMask.count)
{
m_InstanceIdPresenceMask?.Release();
m_InstanceIdPresenceMask = new ComputeBuffer(presenceMaskSizeNeeded, UnsafeUtility.SizeOf(), ComputeBufferType.Structured);
}
if (m_InstanceIdToClassId == null || m_InstanceIdToClassId.count < objectCount)
{
m_InstanceIdToClassId?.Release();
m_InstanceIdToClassId = new ComputeBuffer(objectCount, UnsafeUtility.SizeOf(), ComputeBufferType.Structured);
}
}
protected override void ExecutePass(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult)
{
//If there are no objects to label, skip the pass
if (!m_InstanceIdToClassIdLookup.IsCreated || m_InstanceIdToClassIdLookup.Length == 0)
{
var counts = new NativeArray(LabelingConfiguration.LabelingConfigurations.Count + 1, Allocator.Temp);
OnClassCountReadback(Time.frameCount, counts);
counts.Dispose();
return;
}
m_CamerasRendered.Add(hdCamera.camera);
if (m_IdBuffersNeedUpdating)
{
UpdateIdBufferSizes(m_InstanceIdToClassIdLookup.Capacity);
m_InstanceIdToClassId.SetData(m_InstanceIdToClassIdLookup.AsArray());
}
//The following section kicks off the four kernels in LabeledObjectHistogram.compute
//clear ClassCounts
cmd.SetComputeBufferParam(m_ComputeShader, 1, "ClassCounts", m_ClassCounts);
cmd.DispatchCompute(m_ComputeShader, 1, m_ClassCounts.count, 1, 1);
//clear InstanceIdPresenceMask
cmd.SetComputeBufferParam(m_ComputeShader, 2, "InstanceIdPresenceMask", m_InstanceIdPresenceMask);
cmd.DispatchCompute(m_ComputeShader, 2, m_InstanceIdPresenceMask.count, 1, 1);
//clear InstanceIdPresenceMask
cmd.SetComputeTextureParam(m_ComputeShader, 0, "SegmentationTexture", SegmentationTexture);
cmd.SetComputeBufferParam(m_ComputeShader, 0, "InstanceIdPresenceMask", m_InstanceIdPresenceMask);
cmd.SetComputeIntParam(m_ComputeShader, "Width", SegmentationTexture.width);
cmd.SetComputeIntParam(m_ComputeShader, "Height", SegmentationTexture.height);
cmd.DispatchCompute(m_ComputeShader, 0, SegmentationTexture.width, SegmentationTexture.height, 1);
//clear InstanceIdPresenceMask
cmd.SetComputeBufferParam(m_ComputeShader, 3, "InstanceIdPresenceMask", m_InstanceIdPresenceMask);
cmd.SetComputeBufferParam(m_ComputeShader, 3, "InstanceIdToClassId", m_InstanceIdToClassId);
cmd.SetComputeBufferParam(m_ComputeShader, 3, "ClassCounts", m_ClassCounts);
cmd.DispatchCompute(m_ComputeShader, 3, m_InstanceIdToClassIdLookup.Length, 1, 1);
var requestFrameCount = Time.frameCount;
cmd.RequestAsyncReadback(m_ClassCounts, request => OnClassCountReadback(requestFrameCount, request.GetData()));
}
protected override void Cleanup()
{
base.Cleanup();
m_InstanceIdPresenceMask?.Dispose();
m_InstanceIdPresenceMask = null;
m_InstanceIdToClassId?.Dispose();
m_InstanceIdToClassId = null;
m_ClassCounts?.Dispose();
m_ClassCounts = null;
WaitForAllRequests();
if (m_InstanceIdToClassIdLookup.IsCreated)
{
m_InstanceIdToClassIdLookup.Dispose();
m_InstanceIdToClassIdLookup = default;
}
}
internal event Action, IReadOnlyList, int> ClassCountsReceived;
Unity.Simulation.Logger m_DataLogger;
void OnClassCountReadback(int requestFrameCount, NativeArray counts)
{
#if PERCEPTION_DEBUG
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Histogram data. Frame {0}", requestFrameCount.ToString());
for (int i = 0; i < LabelingConfiguration.LabelingConfigurations.Count; i++)
{
sb.AppendFormat("{0}: {1}", LabelingConfiguration.LabelingConfigurations[i].label,
counts[i + 1].ToString());
sb.AppendLine();
}
Debug.Log(sb);
#endif
if (WriteToLog)
{
//This is a bad pattern. We need to be able to log a JSON string without tons of allocations
var sensorMetric = new SensorMetric() {
FrameId = Time.frameCount
};
var sensorMetricData = new MetricData(
"sensor_metric",
sensorMetric);
for (int i = 0; i < LabelingConfiguration.LabelingConfigurations.Count; i++)
{
sensorMetric.SegmentedHistogram.Add(new ObjectCountEntry
{
Label = LabelingConfiguration.LabelingConfigurations[i].label,
Count = counts[i + 1]
});
}
m_DataLogger.Log(sensorMetricData);
}
ClassCountsReceived?.Invoke(new NativeSlice(counts, 1), LabelingConfiguration.LabelingConfigurations, requestFrameCount);
}
public void WaitForAllRequests()
{
var commandBuffer = CommandBufferPool.Get("LabelHistorgramCleanup");
commandBuffer.WaitAllAsyncReadbackRequests();
Graphics.ExecuteCommandBuffer(commandBuffer);
CommandBufferPool.Release(commandBuffer);
}
}
}
#endif