You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
320 lines
16 KiB
320 lines
16 KiB
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Structure defining level offsets.
|
|
/// </summary>
|
|
[GenerateHLSL]
|
|
public struct LevelOffsets
|
|
{
|
|
/// <summary> Number of levels. </summary>
|
|
public uint count;
|
|
|
|
/// <summary> Level offset. </summary>
|
|
public uint offset;
|
|
|
|
/// <summary> Parent level offset. </summary>
|
|
public uint parentOffset;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Utility for adapting to render graph usage.
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>The prefix sum result.</summary>
|
|
public BufferHandle output => prefixBuffer0;
|
|
|
|
/// <summary>
|
|
/// Creates the render graph buffer resources from an input count.
|
|
/// </summary>
|
|
/// <param name="newMaxElementCount">The maximum number of elements that the buffer will support.</param>
|
|
/// <param name="renderGraph">Render Graph</param>
|
|
/// <param name="builder">Render Graph Builder</param>
|
|
/// <param name="outputIsTemp">Whether or not to allocate a transient resource.</param>
|
|
/// <returns>The created Render Graph Resources.</returns>
|
|
[Obsolete("This Create signature is deprecated and will be removed in the future. Please use Create(IBaseRenderGraphBuilder) instead. #from(6000.3)")]
|
|
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;
|
|
}
|
|
|
|
#pragma warning disable CS0618 // Type or member is obsolete
|
|
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<LevelOffsets>(), 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;
|
|
}
|
|
#pragma warning restore CS0618 // Type or member is obsolete
|
|
|
|
/// <summary>
|
|
/// Creates the render graph buffer resources from an input count.
|
|
/// </summary>
|
|
/// <param name="newMaxElementCount">The maximum number of elements that the buffer will support.</param>
|
|
/// <param name="renderGraph">Render Graph</param>
|
|
/// <param name="builder">Render Graph Builder</param>
|
|
/// <param name="outputIsTemp">Whether or not to allocate a transient resource.</param>
|
|
/// <returns>The created Render Graph Resources.</returns>
|
|
public static RenderGraphResources Create(int newMaxElementCount, RenderGraph renderGraph, IBaseRenderGraphBuilder builder, bool outputIsTemp = false)
|
|
{
|
|
var resources = new RenderGraphResources();
|
|
resources.Initialize(newMaxElementCount, renderGraph, builder, outputIsTemp);
|
|
return resources;
|
|
}
|
|
|
|
void Initialize(int newMaxElementCount, RenderGraph renderGraph, IBaseRenderGraphBuilder 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" };
|
|
if (outputIsTemp)
|
|
prefixBuffer0 = builder.CreateTransientBuffer(prefixBuffer0Desc);
|
|
else
|
|
{
|
|
prefixBuffer0 = renderGraph.CreateBuffer(prefixBuffer0Desc);
|
|
builder.UseBuffer(prefixBuffer0, AccessFlags.Write);
|
|
}
|
|
|
|
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<LevelOffsets>(), 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Data structure containing the runtime resources that are bound by the command buffer.
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>The prefix sum result.</summary>
|
|
public GraphicsBuffer output => prefixBuffer0;
|
|
|
|
/// <summary>
|
|
/// Allocate support resources to accomodate a max count.
|
|
/// </summary>
|
|
/// <param name="maxElementCount">The max element count.</param>
|
|
/// <returns>The created support resources.</returns>
|
|
public static SupportResources Create(int maxElementCount)
|
|
{
|
|
var resources = new SupportResources() { alignedElementCount = 0, ownsResources = true };
|
|
resources.Resize(maxElementCount);
|
|
return resources;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load supporting resources from Render Graph Resources.
|
|
/// </summary>
|
|
/// <param name="shaderGraphResources">Render Graph Resources</param>
|
|
/// <returns>The created support resources.</returns>
|
|
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<LevelOffsets>());
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dispose the supporting resources.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Arguments for a direct prefix sum.
|
|
/// </summary>
|
|
public struct DirectArgs
|
|
{
|
|
/// <summary>An inclusive or exclusive prefix sum.</summary>
|
|
public bool exclusive;
|
|
/// <summary>The size of the input list.</summary>
|
|
public int inputCount;
|
|
/// <summary>The input list.</summary>
|
|
public GraphicsBuffer input;
|
|
/// <summary>Required runtime resources.</summary>
|
|
public SupportResources supportResources;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Arguments for an indirect prefix sum.
|
|
/// </summary>
|
|
public struct IndirectDirectArgs
|
|
{
|
|
/// <summary>An inclusive or exclusive prefix sum.</summary>
|
|
public bool exclusive;
|
|
/// <summary>Byte offset of the count inside the input count buffer.</summary>
|
|
public int inputCountBufferByteOffset;
|
|
/// <summary>GPU buffer defining the size of the input list.</summary>
|
|
public ComputeBuffer inputCountBuffer;
|
|
/// <summary>The input list.</summary>
|
|
public GraphicsBuffer input;
|
|
/// <summary>Required runtime resources.</summary>
|
|
public SupportResources supportResources;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Structure defining any required assets used by the GPU sort.
|
|
/// </summary>
|
|
public struct SystemResources
|
|
{
|
|
/// <summary>
|
|
/// The compute asset that defines all of the kernels for the GPU prefix sum.
|
|
/// </summary>
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
}
|