A game about forced loneliness, made by TACStudios
1using System;
2
3namespace UnityEngine.Rendering
4{
5 /// <summary>
6 /// Utility class for computing inclusive or exclusive prefix sums, directly or indirectly dispatched on the GPU.
7 /// </summary>
8 public partial struct GPUPrefixSum
9 {
10 private SystemResources resources;
11
12 /// <summary>
13 /// Initializes a re-usable GPU prefix sum instance.
14 /// </summary>
15 /// <param name="resources">The required system resources.</param>
16 public GPUPrefixSum(SystemResources resources)
17 {
18 this.resources = resources;
19 this.resources.LoadKernels();
20 }
21
22 Vector4 PackPrefixSumArgs(int a, int b, int c, int d)
23 {
24 unsafe
25 {
26 return new Vector4(
27 *((float*)&a),
28 *((float*)&b),
29 *((float*)&c),
30 *((float*)&d));
31 }
32 }
33
34 internal void ExecuteCommonIndirect(CommandBuffer cmdBuffer, GraphicsBuffer inputBuffer, in SupportResources supportResources, bool isExclusive)
35 {
36 int sumOnGroupKernel = isExclusive ? resources.kernelPrefixSumOnGroupExclusive : resources.kernelPrefixSumOnGroup;
37 int sumResolveParentKernel = isExclusive ? resources.kernelPrefixSumResolveParentExclusive : resources.kernelPrefixSumResolveParent;
38
39 //hierarchy up
40 for (int levelId = 0; levelId < supportResources.maxLevelCount; ++levelId)
41 {
42 var packedArgs = PackPrefixSumArgs(0, 0, 0, levelId);
43 cmdBuffer.SetComputeVectorParam(resources.computeAsset, ShaderIDs._PrefixSumIntArgs, packedArgs);
44
45 if (levelId == 0)
46 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumOnGroupKernel, ShaderIDs._InputBuffer, inputBuffer);
47 else
48 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumOnGroupKernel, ShaderIDs._InputBuffer, supportResources.prefixBuffer1);
49
50 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumOnGroupKernel, ShaderIDs._TotalLevelsBuffer, supportResources.totalLevelCountBuffer);
51 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumOnGroupKernel, ShaderIDs._LevelsOffsetsBuffer, supportResources.levelOffsetBuffer);
52 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumOnGroupKernel, ShaderIDs._OutputBuffer, supportResources.prefixBuffer0);
53 cmdBuffer.DispatchCompute(resources.computeAsset, sumOnGroupKernel, supportResources.indirectDispatchArgsBuffer, (uint)(levelId * ShaderDefs.ArgsBufferStride * 4));
54
55 if (levelId == supportResources.maxLevelCount - 1)
56 continue;
57
58 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelPrefixSumNextInput, ShaderIDs._InputBuffer, supportResources.prefixBuffer0);
59 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelPrefixSumNextInput, ShaderIDs._LevelsOffsetsBuffer, supportResources.levelOffsetBuffer);
60 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelPrefixSumNextInput, ShaderIDs._OutputBuffer, supportResources.prefixBuffer1);
61 cmdBuffer.DispatchCompute(resources.computeAsset, resources.kernelPrefixSumNextInput, supportResources.indirectDispatchArgsBuffer, (uint)((levelId + 1) * ShaderDefs.ArgsBufferStride * 4));
62 }
63
64 //down the hierarchy
65 for (int levelId = supportResources.maxLevelCount - 1; levelId >= 1; --levelId)
66 {
67 var packedArgs = PackPrefixSumArgs(0, 0, 0, levelId);
68 cmdBuffer.SetComputeVectorParam(resources.computeAsset, ShaderIDs._PrefixSumIntArgs, packedArgs);
69 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumResolveParentKernel, ShaderIDs._InputBuffer, inputBuffer);
70 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumResolveParentKernel, ShaderIDs._OutputBuffer, supportResources.prefixBuffer0);
71 cmdBuffer.SetComputeBufferParam(resources.computeAsset, sumResolveParentKernel, ShaderIDs._LevelsOffsetsBuffer, supportResources.levelOffsetBuffer);
72 cmdBuffer.DispatchCompute(resources.computeAsset, sumResolveParentKernel, supportResources.indirectDispatchArgsBuffer, (uint)(((levelId - 1) * ShaderDefs.ArgsBufferStride + ShaderDefs.ArgsBufferLower) * 4));
73 }
74 }
75
76 /// <summary>
77 /// Prefix sum a list of data from a CPU-defined count.
78 /// </summary>
79 /// <param name="cmdBuffer">Command Buffer for recording the prefix sum commands.</param>
80 /// <param name="arguments">Runtime arguments for the prefix sum.</param>
81 /// <exception cref="Exception">When the input data is invalid.</exception>
82 public void DispatchDirect(CommandBuffer cmdBuffer, in DirectArgs arguments)
83 {
84 if (arguments.supportResources.prefixBuffer0 == null || arguments.supportResources.prefixBuffer1 == null)
85 throw new Exception("Support resources are not valid.");
86
87 if (arguments.input == null)
88 throw new Exception("Input source buffer cannot be null.");
89
90 if (arguments.inputCount > arguments.supportResources.alignedElementCount)
91 throw new Exception("Input count exceeds maximum count of support resources. Ensure to create support resources with enough space.");
92
93 //Generate level offsets first, from const value.
94 var packedArgs = PackPrefixSumArgs(arguments.inputCount, arguments.supportResources.maxLevelCount, 0, 0);
95 cmdBuffer.SetComputeVectorParam(resources.computeAsset, ShaderIDs._PrefixSumIntArgs, packedArgs);
96 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromConst, ShaderIDs._OutputLevelsOffsetsBuffer, arguments.supportResources.levelOffsetBuffer);
97 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromConst, ShaderIDs._OutputDispatchLevelArgsBuffer, arguments.supportResources.indirectDispatchArgsBuffer);
98 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromConst, ShaderIDs._OutputTotalLevelsBuffer, arguments.supportResources.totalLevelCountBuffer);
99 cmdBuffer.DispatchCompute(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromConst, 1, 1, 1);
100
101 ExecuteCommonIndirect(cmdBuffer, arguments.input, arguments.supportResources, arguments.exclusive);
102 }
103
104 /// <summary>
105 /// Prefix sum a list of data from a GPU-defined count.
106 /// </summary>
107 /// <param name="cmdBuffer">Command Buffer for recording the prefix sum commands.</param>
108 /// <param name="arguments">Runtime arguments for the prefix sum.</param>
109 /// <exception cref="Exception">When the input data is invalid.</exception>
110 public void DispatchIndirect(CommandBuffer cmdBuffer, in IndirectDirectArgs arguments)
111 {
112 if (arguments.supportResources.prefixBuffer0 == null || arguments.supportResources.prefixBuffer1 == null)
113 throw new Exception("Support resources are not valid.");
114
115 if (arguments.input == null || arguments.inputCountBuffer == null)
116 throw new Exception("Input source buffer and inputCountBuffer cannot be null.");
117
118 //Generate level offsets first, from const value.
119 var packedArgs = PackPrefixSumArgs(0, arguments.supportResources.maxLevelCount, arguments.inputCountBufferByteOffset, 0);
120 cmdBuffer.SetComputeVectorParam(resources.computeAsset, ShaderIDs._PrefixSumIntArgs, packedArgs);
121 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromBuffer, ShaderIDs._InputCountBuffer, arguments.inputCountBuffer);
122 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromBuffer, ShaderIDs._OutputLevelsOffsetsBuffer, arguments.supportResources.levelOffsetBuffer);
123 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromBuffer, ShaderIDs._OutputDispatchLevelArgsBuffer, arguments.supportResources.indirectDispatchArgsBuffer);
124 cmdBuffer.SetComputeBufferParam(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromBuffer, ShaderIDs._OutputTotalLevelsBuffer, arguments.supportResources.totalLevelCountBuffer);
125 cmdBuffer.DispatchCompute(resources.computeAsset, resources.kernelCalculateLevelDispatchArgsFromBuffer, 1, 1, 1);
126
127 ExecuteCommonIndirect(cmdBuffer, arguments.input, arguments.supportResources, arguments.exclusive);
128 }
129 }
130}