using System;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering
{
public partial struct GPUPrefixSum
{
[GenerateHLSL]
internal static class ShaderDefs
{
public const int GroupSize = 128;
// Stride of the indirect arguement buffer in uints, the buffer is split into two sections dispatch options ( a lower or upper arguement set )
public const int ArgsBufferStride = 16;
public const int ArgsBufferUpper = 0;
public const int ArgsBufferLower = 8;
public static int DivUpGroup(int value)
{
return (value + GroupSize - 1) / GroupSize;
}
public static int AlignUpGroup(int value)
{
return DivUpGroup(value) * GroupSize;
}
public static void CalculateTotalBufferSize(int maxElementCount, out int totalSize, out int levelCounts)
{
int alignedSupportMaxCount = AlignUpGroup(maxElementCount);
totalSize = alignedSupportMaxCount;
levelCounts = 1;
while (alignedSupportMaxCount > GroupSize)
{
alignedSupportMaxCount = AlignUpGroup(DivUpGroup(alignedSupportMaxCount));
totalSize += alignedSupportMaxCount;
++levelCounts;
}
}
}
///
/// Structure defining level offsets.
///
[GenerateHLSL]
public struct LevelOffsets
{
/// Number of levels.
public uint count;
/// Level offset.
public uint offset;
/// Parent level offset.
public uint parentOffset;
}
///
/// Utility for adapting to render graph usage.
///
public struct RenderGraphResources
{
internal int alignedElementCount;
internal int maxBufferCount;
internal int maxLevelCount;
internal BufferHandle prefixBuffer0;
internal BufferHandle prefixBuffer1;
internal BufferHandle totalLevelCountBuffer;
internal BufferHandle levelOffsetBuffer;
internal BufferHandle indirectDispatchArgsBuffer;
/// The prefix sum result.
public BufferHandle output => prefixBuffer0;
///
/// Creates the render graph buffer resources from an input count.
///
/// The maximum number of elements that the buffer will support.
/// Render Graph
/// Render Graph Builder
/// Whether or not to allocate a transient resource.
/// The created Render Graph Resources.
public static RenderGraphResources Create(int newMaxElementCount, RenderGraph renderGraph, RenderGraphBuilder builder, bool outputIsTemp = false)
{
var resources = new RenderGraphResources();
resources.Initialize(newMaxElementCount, renderGraph, builder, outputIsTemp);
return resources;
}
void Initialize(int newMaxElementCount, RenderGraph renderGraph, RenderGraphBuilder builder, bool outputIsTemp = false)
{
newMaxElementCount = Math.Max(newMaxElementCount, 1);
ShaderDefs.CalculateTotalBufferSize(newMaxElementCount, out int totalSize, out int levelCounts);
var prefixBuffer0Desc = new BufferDesc(totalSize, 4, GraphicsBuffer.Target.Raw) { name = "prefixBuffer0" };
prefixBuffer0 = outputIsTemp ? builder.CreateTransientBuffer(prefixBuffer0Desc) : builder.WriteBuffer(renderGraph.CreateBuffer(prefixBuffer0Desc));
prefixBuffer1 = builder.CreateTransientBuffer(new BufferDesc(newMaxElementCount, 4, GraphicsBuffer.Target.Raw) { name = "prefixBuffer1" });
totalLevelCountBuffer = builder.CreateTransientBuffer(new BufferDesc(1, 4, GraphicsBuffer.Target.Raw) { name = "totalLevelCountBuffer" });
levelOffsetBuffer = builder.CreateTransientBuffer(new BufferDesc(levelCounts, System.Runtime.InteropServices.Marshal.SizeOf(), GraphicsBuffer.Target.Structured) { name = "levelOffsetBuffer" });
indirectDispatchArgsBuffer = builder.CreateTransientBuffer(new BufferDesc(ShaderDefs.ArgsBufferStride * levelCounts, sizeof(uint), GraphicsBuffer.Target.Structured | GraphicsBuffer.Target.IndirectArguments) { name = "indirectDispatchArgsBuffer" });//3 arguments for upp dispatch, 3 arguments for lower dispatch
alignedElementCount = ShaderDefs.AlignUpGroup(newMaxElementCount);
maxBufferCount = totalSize;
maxLevelCount = levelCounts;
}
}
///
/// Data structure containing the runtime resources that are bound by the command buffer.
///
public struct SupportResources
{
internal bool ownsResources;
internal int alignedElementCount;
internal int maxBufferCount;
internal int maxLevelCount;
internal GraphicsBuffer prefixBuffer0;
internal GraphicsBuffer prefixBuffer1;
internal GraphicsBuffer totalLevelCountBuffer;
internal GraphicsBuffer levelOffsetBuffer;
internal GraphicsBuffer indirectDispatchArgsBuffer;
/// The prefix sum result.
public GraphicsBuffer output => prefixBuffer0;
///
/// Allocate support resources to accomodate a max count.
///
/// The max element count.
/// The created support resources.
public static SupportResources Create(int maxElementCount)
{
var resources = new SupportResources() { alignedElementCount = 0, ownsResources = true };
resources.Resize(maxElementCount);
return resources;
}
///
/// Load supporting resources from Render Graph Resources.
///
/// Render Graph Resources
/// The created support resources.
public static SupportResources Load(RenderGraphResources shaderGraphResources)
{
var resources = new SupportResources() { alignedElementCount = 0, ownsResources = false };
resources.LoadFromShaderGraph(shaderGraphResources);
return resources;
}
internal void Resize(int newMaxElementCount)
{
if (!ownsResources)
throw new Exception("Cannot resize resources unless they are owned. Use GpuPrefixSumSupportResources.Create() for this.");
newMaxElementCount = Math.Max(newMaxElementCount, 1); //at bare minimum support a single group.
if (alignedElementCount >= newMaxElementCount)
return;
Dispose();
ShaderDefs.CalculateTotalBufferSize(newMaxElementCount, out int totalSize, out int levelCounts);
alignedElementCount = ShaderDefs.AlignUpGroup(newMaxElementCount);
maxBufferCount = totalSize;
maxLevelCount = levelCounts;
prefixBuffer0 = new GraphicsBuffer(GraphicsBuffer.Target.Raw, totalSize, 4);
prefixBuffer1 = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newMaxElementCount, 4);
totalLevelCountBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, 1, 4);
levelOffsetBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, levelCounts, System.Runtime.InteropServices.Marshal.SizeOf());
indirectDispatchArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, ShaderDefs.ArgsBufferStride * levelCounts, sizeof(uint));//3 arguments for upp dispatch, 3 arguments for lower dispatch
}
void LoadFromShaderGraph(RenderGraphResources shaderGraphResources)
{
alignedElementCount = shaderGraphResources.alignedElementCount;
maxBufferCount = shaderGraphResources.maxBufferCount;
maxLevelCount = shaderGraphResources.maxLevelCount;
prefixBuffer0 = (GraphicsBuffer)shaderGraphResources.prefixBuffer0;
prefixBuffer1 = (GraphicsBuffer)shaderGraphResources.prefixBuffer1;
totalLevelCountBuffer = (GraphicsBuffer)shaderGraphResources.totalLevelCountBuffer;
levelOffsetBuffer = (GraphicsBuffer)shaderGraphResources.levelOffsetBuffer;
indirectDispatchArgsBuffer = (GraphicsBuffer)shaderGraphResources.indirectDispatchArgsBuffer;
}
///
/// Dispose the supporting resources.
///
public void Dispose()
{
if (alignedElementCount == 0 || !ownsResources)
return;
alignedElementCount = 0;
void TryFreeBuffer(GraphicsBuffer resource)
{
if (resource != null)
{
resource.Dispose();
resource = null;
}
}
TryFreeBuffer(prefixBuffer0);
TryFreeBuffer(prefixBuffer1);
TryFreeBuffer(levelOffsetBuffer);
TryFreeBuffer(indirectDispatchArgsBuffer);
TryFreeBuffer(totalLevelCountBuffer);
}
}
///
/// Arguments for a direct prefix sum.
///
public struct DirectArgs
{
/// An inclusive or exclusive prefix sum.
public bool exclusive;
/// The size of the input list.
public int inputCount;
/// The input list.
public GraphicsBuffer input;
/// Required runtime resources.
public SupportResources supportResources;
}
///
/// Arguments for an indirect prefix sum.
///
public struct IndirectDirectArgs
{
/// An inclusive or exclusive prefix sum.
public bool exclusive;
/// Byte offset of the count inside the input count buffer.
public int inputCountBufferByteOffset;
/// GPU buffer defining the size of the input list.
public ComputeBuffer inputCountBuffer;
/// The input list.
public GraphicsBuffer input;
/// Required runtime resources.
public SupportResources supportResources;
}
///
/// Structure defining any required assets used by the GPU sort.
///
public struct SystemResources
{
///
/// The compute asset that defines all of the kernels for the GPU prefix sum.
///
public ComputeShader computeAsset;
internal int kernelCalculateLevelDispatchArgsFromConst;
internal int kernelCalculateLevelDispatchArgsFromBuffer;
internal int kernelPrefixSumOnGroup;
internal int kernelPrefixSumOnGroupExclusive;
internal int kernelPrefixSumNextInput;
internal int kernelPrefixSumResolveParent;
internal int kernelPrefixSumResolveParentExclusive;
internal void LoadKernels()
{
if (computeAsset == null)
return;
kernelCalculateLevelDispatchArgsFromConst = computeAsset.FindKernel("MainCalculateLevelDispatchArgsFromConst");
kernelCalculateLevelDispatchArgsFromBuffer = computeAsset.FindKernel("MainCalculateLevelDispatchArgsFromBuffer");
kernelPrefixSumOnGroup = computeAsset.FindKernel("MainPrefixSumOnGroup");
kernelPrefixSumOnGroupExclusive = computeAsset.FindKernel("MainPrefixSumOnGroupExclusive");
kernelPrefixSumNextInput = computeAsset.FindKernel("MainPrefixSumNextInput");
kernelPrefixSumResolveParent = computeAsset.FindKernel("MainPrefixSumResolveParent");
kernelPrefixSumResolveParentExclusive = computeAsset.FindKernel("MainPrefixSumResolveParentExclusive");
}
}
}
}