// Uncomment this line to activate the GPUInlineDebugDrawer
// Do not overload the RenderGraph with empty function is not needed.
// #define ENABLE_GPU_INLINE_DEBUG_DRAWER
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.HighDefinition
{
[GenerateHLSL(PackingRules.Exact, false)]
internal struct GPUInlineDebugDrawerLine
{
public Vector4 start;
public Vector4 end;
public Color startColor;
public Color endColor;
};
#if UNITY_EDITOR
///
/// This helper allow us to draw debug primitive from Shader 'Inline'.
///
/// To use include the helper on the shader:
/// #include "Packages/com.unity.render-pipelines.high-definition/Runtime/Debug/GPUInlineDebugDrawer.hlsl"
///
/// And:
///
/// - Line World Space:
/// void GPUInlineDebugDrawer_AddLineWS(float4 start, float4 end, float3 startColor, float3 endColor);
/// void GPUInlineDebugDrawer_AddLineWS(float4 start, float4 end, float3 color = float3(1, 0, 0));
/// void GPUInlineDebugDrawer_AddLineWS(float3 start, float3 end, float3 color = float3(1, 0, 0));
/// void GPUInlineDebugDrawer_AddLineWS(float3 start, float3 end, float3 startColor, float3 endColor);
/// - Line Clip Space:
/// void GPUInlineDebugDrawer_AddLineCS(float4 start, float4 end, float3 startColor, float3 endColor);
/// void GPUInlineDebugDrawer_AddLineCS(float4 start, float4 end, float3 color = float3(1, 0, 0));
/// void GPUInlineDebugDrawer_AddLineCS(float3 start, float3 end, float3 color = float3(1, 0, 0));
/// void GPUInlineDebugDrawer_AddLineCS(float3 start, float3 end, float3 startColor, float3 endColor);
/// - Plot Ring Buffer:
/// void GPUInlineDebugDrawer_PlotRingBufferAddFloat(float x)
/// will draw a Plot of the Ring Buffer to debug the value x.
/// The Value is between 0.0f and 1.0f;
///
static class GPUInlineDebugDrawer
{
#if ENABLE_GPU_INLINE_DEBUG_DRAWER
static BufferHandle lineWSBuffer;
static BufferHandle lineCSBuffer;
static GraphicsBuffer lineWSIndirectArgs;
static GraphicsBuffer lineCSIndirectArgs;
static GraphicsBuffer plotRingBuffer;
static GraphicsBuffer plotRingBufferStart;
static GraphicsBuffer plotRingBufferEnd;
static BufferHandle plotRingBufferHandle;
static BufferHandle plotRingBufferStartHandle;
static BufferHandle plotRingBufferEndHandle;
static Material m_LineMaterial;
static int m_LineWSNoDepthTestPassID;
static int m_LineCSNoDepthTestPassID;
static int m_PlotRingBufferPassID;
static uint[] m_IndirectLineArgsDefault = new uint[] {
2, // vertices per instance
0, // instance count [Will be overridden]
0, // byte offset of first vertex
0 // byte offset of first instance
};
#endif
///
/// Global constant used for GPUInlineDebugDrawer
/// : If changed need to regenerate headers.
///
[GenerateHLSL(PackingRules.Exact)]
public enum GPUInlineDebugDrawerParams
{
/// Maximum number of line which can be draw.
MaxLines = 4096,
/// Maximum size of the ring buffer for the plot.
MaxPlotRingBuffer = 256
};
///
/// Manually initialize resources needed to render the debug draw.
///
public static void Initialize()
{
#if ENABLE_GPU_INLINE_DEBUG_DRAWER
if (!GraphicsSettings.TryGetRenderPipelineSettings(out var defaultShaders))
{
Debug.LogWarning($"Unable to initialize {nameof(GPUInlineDebugDrawer)} due to missing {nameof(HDRenderPipelineEditorShaders)}");
return;
}
m_LineMaterial = CoreUtils.CreateEngineMaterial(defaultShaders.gpuInlineDebugDrawerLine);
m_LineMaterial.SetOverrideTag("RenderType", "Transparent");
m_LineWSNoDepthTestPassID = m_LineMaterial.FindPass("LineWSNoDepthTest");
m_LineCSNoDepthTestPassID = m_LineMaterial.FindPass("LineCSNoDepthTest");
m_PlotRingBufferPassID = m_LineMaterial.FindPass("PlotRingBufferPass");
lineWSIndirectArgs = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 4, sizeof(int));
lineCSIndirectArgs = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 4, sizeof(int));
plotRingBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, (int)GPUInlineDebugDrawerParams.MaxPlotRingBuffer, sizeof(float));
plotRingBufferStart = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, sizeof(uint));
plotRingBufferEnd = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1, sizeof(uint));
float[] defaultData0fs = new float[(int)GPUInlineDebugDrawerParams.MaxPlotRingBuffer];
System.Array.Clear(defaultData0fs, 0, (int)GPUInlineDebugDrawerParams.MaxPlotRingBuffer);
uint[] defaultData0u = new uint[1];
defaultData0u[0] = 0u;
plotRingBuffer.SetData(defaultData0fs);
plotRingBufferStart.SetData(defaultData0u);
plotRingBufferEnd.SetData(defaultData0u);
#endif
}
///
/// Manually release resources.
///
public static void Dispose()
{
#if ENABLE_GPU_INLINE_DEBUG_DRAWER
void TryFreeBuffer(ref GraphicsBuffer resource)
{
if (resource != null)
{
resource.Dispose();
resource = null;
}
}
TryFreeBuffer(ref lineWSIndirectArgs);
TryFreeBuffer(ref lineCSIndirectArgs);
TryFreeBuffer(ref plotRingBuffer);
TryFreeBuffer(ref plotRingBufferStart);
TryFreeBuffer(ref plotRingBufferEnd);
#endif
}
class GPUInlineDebugDrawerData
{
public BufferHandle lineWSBuffer;
public BufferHandle lineCSBuffer;
public BufferHandle lineWSIndirectArgs;
public BufferHandle lineCSIndirectArgs;
public BufferHandle plotRingBuffer;
public BufferHandle plotRingBufferStart;
public BufferHandle plotRingBufferEnd;
public Vector2 mousePosition;
public Material lineMaterial;
public int lineWSNoDepthTestPassID;
public int lineCSNoDepthTestPassID;
public int plotRingBufferPassID;
}
///
/// Bind resources used during the whole frame.
///
/// Camera used to retrive the size of the screen to use properly mouse position.
/// Current RenderGraph
static public void BindProducers(HDCamera cam, RenderGraph renderGraph)
{
#if ENABLE_GPU_INLINE_DEBUG_DRAWER
using (var builder = renderGraph.AddUnsafePass("GPUInlineDebugDrawer_BindProducers", out var passData))
{
int maxLines = (int)GPUInlineDebugDrawerParams.MaxLines;
lineWSBuffer = renderGraph.CreateBuffer(new BufferDesc(maxLines, Marshal.SizeOf(typeof(GPUInlineDebugDrawerLine)), GraphicsBuffer.Target.Append)
{ name = "GPUInlineDebugDrawerLineWS" });
lineCSBuffer = renderGraph.CreateBuffer(new BufferDesc(maxLines, Marshal.SizeOf(typeof(GPUInlineDebugDrawerLine)), GraphicsBuffer.Target.Append)
{ name = "GPUInlineDebugDrawerLineCS" });
passData.lineWSBuffer = lineWSBuffer;
builder.UseBuffer(passData.lineWSBuffer, AccessFlags.Write);
passData.lineCSBuffer = lineCSBuffer;
builder.UseBuffer(passData.lineCSBuffer, AccessFlags.Write);
plotRingBufferHandle = renderGraph.ImportBuffer(plotRingBuffer);
plotRingBufferStartHandle = renderGraph.ImportBuffer(plotRingBufferStart);
plotRingBufferEndHandle = renderGraph.ImportBuffer(plotRingBufferEnd);
passData.plotRingBuffer = plotRingBufferHandle;
builder.UseBuffer(passData.plotRingBuffer, AccessFlags.Write);
passData.plotRingBufferStart = plotRingBufferStartHandle;
builder.UseBuffer(passData.plotRingBufferStart, AccessFlags.Write);
passData.plotRingBufferEnd = plotRingBufferEndHandle;
builder.UseBuffer(passData.plotRingBufferEnd, AccessFlags.Write);
passData.mousePosition = new Vector2(Mathf.Round(Event.current.mousePosition.x), Mathf.Round(cam.actualHeight - 1 - Event.current.mousePosition.y));
builder.SetRenderFunc(
(GPUInlineDebugDrawerData data, Rendering.RenderGraphModule.UnsafeGraphContext ctx) =>
{
var natCmd = CommandBufferHelpers.GetNativeCommandBuffer(ctx.cmd);
((GraphicsBuffer)data.lineWSBuffer).SetCounterValue(0u);
((GraphicsBuffer)data.lineCSBuffer).SetCounterValue(0u);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawerLinesWSProduce, data.lineWSBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawerLinesCSProduce, data.lineCSBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBuffer, data.plotRingBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBufferStart, data.plotRingBufferStart);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBufferEnd, data.plotRingBufferEnd);
natCmd.SetGlobalVector(HDShaderIDs._GPUInlineDebugDrawerMousePos, data.mousePosition);
});
}
#endif
}
///
/// Do the proper draw, must be called at the end of the frame, after the PostProcess.
///
/// Current RenderGraph
static public void Draw(RenderGraph renderGraph)
{
#if ENABLE_GPU_INLINE_DEBUG_DRAWER
using (var builder = renderGraph.AddUnsafePass("GPUInlineDebugDrawer_Draws", out var passData))
{
passData.lineWSBuffer = lineWSBuffer;
builder.UseBuffer(passData.lineWSBuffer, AccessFlags.Read);
passData.lineCSBuffer = lineCSBuffer;
builder.UseBuffer(passData.lineCSBuffer, AccessFlags.Read);
lineWSIndirectArgs.SetData(m_IndirectLineArgsDefault);
lineCSIndirectArgs.SetData(m_IndirectLineArgsDefault);
passData.lineWSIndirectArgs = renderGraph.ImportBuffer(lineWSIndirectArgs);
builder.UseBuffer(passData.lineWSIndirectArgs, AccessFlags.Write);
passData.lineCSIndirectArgs = renderGraph.ImportBuffer(lineCSIndirectArgs);
builder.UseBuffer(passData.lineCSIndirectArgs, AccessFlags.Write);
passData.plotRingBuffer = plotRingBufferHandle;
builder.UseBuffer(passData.plotRingBuffer, AccessFlags.Read);
passData.plotRingBufferStart = plotRingBufferStartHandle;
builder.UseBuffer(passData.plotRingBufferStart, AccessFlags.Read);
passData.plotRingBufferEnd = plotRingBufferEndHandle;
builder.UseBuffer(passData.plotRingBufferEnd, AccessFlags.Read);
passData.lineMaterial = m_LineMaterial;
passData.lineWSNoDepthTestPassID = m_LineWSNoDepthTestPassID;
passData.lineCSNoDepthTestPassID = m_LineCSNoDepthTestPassID;
passData.plotRingBufferPassID = m_PlotRingBufferPassID;
builder.SetRenderFunc(
(GPUInlineDebugDrawerData data, UnsafeGraphContext ctx) =>
{
var natCmd = CommandBufferHelpers.GetNativeCommandBuffer(ctx.cmd);
natCmd.CopyCounterValue(data.lineWSBuffer, data.lineWSIndirectArgs, sizeof(uint));
natCmd.CopyCounterValue(data.lineCSBuffer, data.lineCSIndirectArgs, sizeof(uint));
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawerLinesWSConsume, data.lineWSBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawerLinesCSConsume, data.lineCSBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBufferRead, data.plotRingBuffer);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBufferStartRead, data.plotRingBufferStart);
natCmd.SetGlobalBuffer(HDShaderIDs._GPUInlineDebugDrawer_PlotRingBufferEndRead, data.plotRingBufferEnd);
// Draw World Space Lines
natCmd.DrawProceduralIndirect(Matrix4x4.identity, data.lineMaterial, data.lineWSNoDepthTestPassID, MeshTopology.Lines, data.lineWSIndirectArgs);
// Draw Clip Space Lines
natCmd.DrawProceduralIndirect(Matrix4x4.identity, data.lineMaterial, data.lineCSNoDepthTestPassID, MeshTopology.Lines, data.lineCSIndirectArgs);
// Draw Plot Ring Buffer
natCmd.DrawProcedural(Matrix4x4.identity, data.lineMaterial, data.plotRingBufferPassID, MeshTopology.LineStrip, (int)GPUInlineDebugDrawerParams.MaxPlotRingBuffer + 5);
});
}
#endif
}
};
#endif
}