144 lines
5.3 KiB
C#
144 lines
5.3 KiB
C#
using System.Diagnostics;
|
|
using UnityEngine;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Unity.Barracuda {
|
|
|
|
internal class ComputeDebugUtils
|
|
{
|
|
/// <summary>
|
|
/// DEBUG ONLY: `debugKernels` allow to track out of bound read/write and assertion in kernels.
|
|
/// When set to true be sure to define KERNEL_ASSERTS or FORCE_DEBUG in the particular kernel(s)
|
|
/// you want to debug (see in DebugUtils.cginc).
|
|
/// Production code should not set this to 'true' as this will significantly degrade performances.
|
|
/// </summary>
|
|
public static bool debugKernels = false;
|
|
|
|
/// <summary>
|
|
/// DEBUG ONLY: if ComputeDebugUtils.debugKernels is true and debugger is attached, debugger will break when a kernel assertion is catch.
|
|
/// </summary>
|
|
public static bool breakOnAssertion = false;
|
|
|
|
//Keep in sync with DebugUtils.cginc KERNEL_ASSERT_CONTEXT defines
|
|
private enum KernelAssertContext
|
|
{
|
|
ReadOnlyTensor_Read = 0,
|
|
ReadWriteTensor_Read = 1,
|
|
ReadWriteTensor_Write = 2,
|
|
SharedTensor_Read = 3,
|
|
Assertion = 4,
|
|
AssertionWithValue = 5
|
|
}
|
|
|
|
static ComputeDebugUtils()
|
|
{
|
|
string[] args = System.Environment.GetCommandLineArgs ();
|
|
for (int i = 0; i < args.Length; i++) {
|
|
if (args [i] == "-barracuda-debug-gpu-kernels")
|
|
{
|
|
debugKernels = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
|
public struct KernelAssertInfo
|
|
{
|
|
public KernelAssertInfo(uint[] data)
|
|
{
|
|
UnityEngine.Debug.Assert(numUintInKernelAssertInfo == data.Length);
|
|
UnityEngine.Debug.Assert(numUintInKernelAssertInfo == 8,
|
|
"Please change KernelAssertInfo constructor if altering the struct.");
|
|
lockValue = data[0];
|
|
lineNumber = data[1];
|
|
context = data[2];
|
|
index = data[3];
|
|
bufferSize = data[4];
|
|
debugValue = data[5];
|
|
padding1 = data[6];
|
|
padding2 = data[7];
|
|
}
|
|
|
|
public readonly uint lockValue;
|
|
public readonly uint lineNumber;
|
|
public readonly uint context;
|
|
public readonly uint index;
|
|
public readonly uint bufferSize;
|
|
public readonly uint debugValue;
|
|
public readonly uint padding1;
|
|
public readonly uint padding2;
|
|
}
|
|
private static readonly int numUintInKernelAssertInfo = Marshal.SizeOf(typeof(KernelAssertInfo))/sizeof(uint);
|
|
|
|
private static ComputeBuffer kernelDebugInfo = null;
|
|
|
|
private static void LogAssertion(KernelAssertInfo info, string kernelName)
|
|
{
|
|
if (info.lockValue != 0)
|
|
{
|
|
string source;
|
|
switch (info.context)
|
|
{
|
|
case (int) KernelAssertContext.ReadOnlyTensor_Read:
|
|
source = $"Out of bound while Reading a ReadonlyTensor of length {info.bufferSize} at index {info.index} (at Tensor.cginc line {info.lineNumber})";
|
|
break;
|
|
case (int) KernelAssertContext.ReadWriteTensor_Read:
|
|
source = $"Out of bound while Reading a ReadWriteTensor of length {info.bufferSize} at index {info.index} (at Tensor.cginc line {info.lineNumber})";
|
|
break;
|
|
case (int) KernelAssertContext.ReadWriteTensor_Write:
|
|
source = $"Out of bound while Writing to a ReadWriteTensor of length {info.bufferSize} at index {info.index} (at Tensor.cginc line {info.lineNumber})";
|
|
break;
|
|
case (int) KernelAssertContext.SharedTensor_Read:
|
|
source = $"Out of bound while Reading a SharedTensor of length {info.bufferSize} at index {info.index} (at Tensor.cginc line {info.lineNumber})";
|
|
break;
|
|
case (int) KernelAssertContext.Assertion:
|
|
source = $"Assertion at line {info.lineNumber}";
|
|
break;
|
|
case (int) KernelAssertContext.AssertionWithValue:
|
|
source = $"Assertion at line {info.lineNumber}, debug value is {info.debugValue}";
|
|
break;
|
|
default:
|
|
source = "Unknown error";
|
|
break;
|
|
}
|
|
|
|
string message = $"{source} in kernel {kernelName}.";
|
|
D.LogError(message);
|
|
|
|
if (breakOnAssertion)
|
|
{
|
|
Debugger.Break();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static void PrepareDispatch()
|
|
{
|
|
//Lazy alloc, will be released by GC.
|
|
if (debugKernels && kernelDebugInfo == null)
|
|
{
|
|
kernelDebugInfo = new ComputeBuffer(1, numUintInKernelAssertInfo*sizeof(uint));
|
|
}
|
|
|
|
if (debugKernels)
|
|
{
|
|
Shader.SetGlobalBuffer("KernelAssertInfoBuffer", kernelDebugInfo);
|
|
kernelDebugInfo.SetData(new uint[numUintInKernelAssertInfo]); //TODO use a kernel to zero out the buffer to avoid a extra sync.
|
|
}
|
|
}
|
|
|
|
public static void VerifyDispatch(string kernelName)
|
|
{
|
|
if (debugKernels)
|
|
{
|
|
UnityEngine.Debug.Assert(kernelDebugInfo != null);
|
|
var data = new uint[numUintInKernelAssertInfo];
|
|
kernelDebugInfo.GetData(data, 0, 0, numUintInKernelAssertInfo);
|
|
LogAssertion(new KernelAssertInfo(data), kernelName);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Unity.Barracuda
|