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"); } } } }