A game about forced loneliness, made by TACStudios
1using System;
2using UnityEngine.Rendering.RenderGraphModule;
3
4namespace UnityEngine.Rendering
5{
6 public partial struct GPUPrefixSum
7 {
8 [GenerateHLSL]
9 internal static class ShaderDefs
10 {
11 public const int GroupSize = 128;
12
13 // Stride of the indirect arguement buffer in uints, the buffer is split into two sections dispatch options ( a lower or upper arguement set )
14 public const int ArgsBufferStride = 16;
15 public const int ArgsBufferUpper = 0;
16 public const int ArgsBufferLower = 8;
17
18 public static int DivUpGroup(int value)
19 {
20 return (value + GroupSize - 1) / GroupSize;
21 }
22
23 public static int AlignUpGroup(int value)
24 {
25 return DivUpGroup(value) * GroupSize;
26 }
27
28 public static void CalculateTotalBufferSize(int maxElementCount, out int totalSize, out int levelCounts)
29 {
30 int alignedSupportMaxCount = AlignUpGroup(maxElementCount);
31 totalSize = alignedSupportMaxCount;
32 levelCounts = 1;
33 while (alignedSupportMaxCount > GroupSize)
34 {
35 alignedSupportMaxCount = AlignUpGroup(DivUpGroup(alignedSupportMaxCount));
36 totalSize += alignedSupportMaxCount;
37 ++levelCounts;
38 }
39 }
40 }
41
42 /// <summary>
43 /// Structure defining level offsets.
44 /// </summary>
45 [GenerateHLSL]
46 public struct LevelOffsets
47 {
48 /// <summary> Number of levels. </summary>
49 public uint count;
50
51 /// <summary> Level offset. </summary>
52 public uint offset;
53
54 /// <summary> Parent level offset. </summary>
55 public uint parentOffset;
56 }
57
58 /// <summary>
59 /// Utility for adapting to render graph usage.
60 /// </summary>
61 public struct RenderGraphResources
62 {
63 internal int alignedElementCount;
64 internal int maxBufferCount;
65 internal int maxLevelCount;
66
67 internal BufferHandle prefixBuffer0;
68 internal BufferHandle prefixBuffer1;
69 internal BufferHandle totalLevelCountBuffer;
70 internal BufferHandle levelOffsetBuffer;
71 internal BufferHandle indirectDispatchArgsBuffer;
72
73 /// <summary>The prefix sum result.</summary>
74 public BufferHandle output => prefixBuffer0;
75
76 /// <summary>
77 /// Creates the render graph buffer resources from an input count.
78 /// </summary>
79 /// <param name="newMaxElementCount">The maximum number of elements that the buffer will support.</param>
80 /// <param name="renderGraph">Render Graph</param>
81 /// <param name="builder">Render Graph Builder</param>
82 /// <param name="outputIsTemp">Whether or not to allocate a transient resource.</param>
83 /// <returns>The created Render Graph Resources.</returns>
84 public static RenderGraphResources Create(int newMaxElementCount, RenderGraph renderGraph, RenderGraphBuilder builder, bool outputIsTemp = false)
85 {
86 var resources = new RenderGraphResources();
87 resources.Initialize(newMaxElementCount, renderGraph, builder, outputIsTemp);
88 return resources;
89 }
90
91 void Initialize(int newMaxElementCount, RenderGraph renderGraph, RenderGraphBuilder builder, bool outputIsTemp = false)
92 {
93 newMaxElementCount = Math.Max(newMaxElementCount, 1);
94 ShaderDefs.CalculateTotalBufferSize(newMaxElementCount, out int totalSize, out int levelCounts);
95
96 var prefixBuffer0Desc = new BufferDesc(totalSize, 4, GraphicsBuffer.Target.Raw) { name = "prefixBuffer0" };
97 prefixBuffer0 = outputIsTemp ? builder.CreateTransientBuffer(prefixBuffer0Desc) : builder.WriteBuffer(renderGraph.CreateBuffer(prefixBuffer0Desc));
98 prefixBuffer1 = builder.CreateTransientBuffer(new BufferDesc(newMaxElementCount, 4, GraphicsBuffer.Target.Raw) { name = "prefixBuffer1" });
99 totalLevelCountBuffer = builder.CreateTransientBuffer(new BufferDesc(1, 4, GraphicsBuffer.Target.Raw) { name = "totalLevelCountBuffer" });
100 levelOffsetBuffer = builder.CreateTransientBuffer(new BufferDesc(levelCounts, System.Runtime.InteropServices.Marshal.SizeOf<LevelOffsets>(), GraphicsBuffer.Target.Structured) { name = "levelOffsetBuffer" });
101 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
102 alignedElementCount = ShaderDefs.AlignUpGroup(newMaxElementCount);
103 maxBufferCount = totalSize;
104 maxLevelCount = levelCounts;
105 }
106 }
107
108 /// <summary>
109 /// Data structure containing the runtime resources that are bound by the command buffer.
110 /// </summary>
111 public struct SupportResources
112 {
113 internal bool ownsResources;
114 internal int alignedElementCount;
115 internal int maxBufferCount;
116 internal int maxLevelCount;
117
118 internal GraphicsBuffer prefixBuffer0;
119 internal GraphicsBuffer prefixBuffer1;
120 internal GraphicsBuffer totalLevelCountBuffer;
121 internal GraphicsBuffer levelOffsetBuffer;
122 internal GraphicsBuffer indirectDispatchArgsBuffer;
123
124 /// <summary>The prefix sum result.</summary>
125 public GraphicsBuffer output => prefixBuffer0;
126
127 /// <summary>
128 /// Allocate support resources to accomodate a max count.
129 /// </summary>
130 /// <param name="maxElementCount">The max element count.</param>
131 /// <returns>The created support resources.</returns>
132 public static SupportResources Create(int maxElementCount)
133 {
134 var resources = new SupportResources() { alignedElementCount = 0, ownsResources = true };
135 resources.Resize(maxElementCount);
136 return resources;
137 }
138
139 /// <summary>
140 /// Load supporting resources from Render Graph Resources.
141 /// </summary>
142 /// <param name="shaderGraphResources">Render Graph Resources</param>
143 /// <returns>The created support resources.</returns>
144 public static SupportResources Load(RenderGraphResources shaderGraphResources)
145 {
146 var resources = new SupportResources() { alignedElementCount = 0, ownsResources = false };
147 resources.LoadFromShaderGraph(shaderGraphResources);
148 return resources;
149 }
150
151 internal void Resize(int newMaxElementCount)
152 {
153 if (!ownsResources)
154 throw new Exception("Cannot resize resources unless they are owned. Use GpuPrefixSumSupportResources.Create() for this.");
155
156 newMaxElementCount = Math.Max(newMaxElementCount, 1); //at bare minimum support a single group.
157 if (alignedElementCount >= newMaxElementCount)
158 return;
159
160 Dispose();
161 ShaderDefs.CalculateTotalBufferSize(newMaxElementCount, out int totalSize, out int levelCounts);
162
163 alignedElementCount = ShaderDefs.AlignUpGroup(newMaxElementCount);
164 maxBufferCount = totalSize;
165 maxLevelCount = levelCounts;
166
167 prefixBuffer0 = new GraphicsBuffer(GraphicsBuffer.Target.Raw, totalSize, 4);
168 prefixBuffer1 = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newMaxElementCount, 4);
169 totalLevelCountBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, 1, 4);
170 levelOffsetBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, levelCounts, System.Runtime.InteropServices.Marshal.SizeOf<LevelOffsets>());
171 indirectDispatchArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, ShaderDefs.ArgsBufferStride * levelCounts, sizeof(uint));//3 arguments for upp dispatch, 3 arguments for lower dispatch
172 }
173
174 void LoadFromShaderGraph(RenderGraphResources shaderGraphResources)
175 {
176 alignedElementCount = shaderGraphResources.alignedElementCount;
177 maxBufferCount = shaderGraphResources.maxBufferCount;
178 maxLevelCount = shaderGraphResources.maxLevelCount;
179
180 prefixBuffer0 = (GraphicsBuffer)shaderGraphResources.prefixBuffer0;
181 prefixBuffer1 = (GraphicsBuffer)shaderGraphResources.prefixBuffer1;
182 totalLevelCountBuffer = (GraphicsBuffer)shaderGraphResources.totalLevelCountBuffer;
183 levelOffsetBuffer = (GraphicsBuffer)shaderGraphResources.levelOffsetBuffer;
184 indirectDispatchArgsBuffer = (GraphicsBuffer)shaderGraphResources.indirectDispatchArgsBuffer;
185 }
186
187 /// <summary>
188 /// Dispose the supporting resources.
189 /// </summary>
190 public void Dispose()
191 {
192 if (alignedElementCount == 0 || !ownsResources)
193 return;
194
195 alignedElementCount = 0;
196
197 void TryFreeBuffer(GraphicsBuffer resource)
198 {
199 if (resource != null)
200 {
201 resource.Dispose();
202 resource = null;
203 }
204 }
205
206 TryFreeBuffer(prefixBuffer0);
207 TryFreeBuffer(prefixBuffer1);
208 TryFreeBuffer(levelOffsetBuffer);
209 TryFreeBuffer(indirectDispatchArgsBuffer);
210 TryFreeBuffer(totalLevelCountBuffer);
211 }
212 }
213
214 /// <summary>
215 /// Arguments for a direct prefix sum.
216 /// </summary>
217 public struct DirectArgs
218 {
219 /// <summary>An inclusive or exclusive prefix sum.</summary>
220 public bool exclusive;
221 /// <summary>The size of the input list.</summary>
222 public int inputCount;
223 /// <summary>The input list.</summary>
224 public GraphicsBuffer input;
225 /// <summary>Required runtime resources.</summary>
226 public SupportResources supportResources;
227 }
228
229 /// <summary>
230 /// Arguments for an indirect prefix sum.
231 /// </summary>
232 public struct IndirectDirectArgs
233 {
234 /// <summary>An inclusive or exclusive prefix sum.</summary>
235 public bool exclusive;
236 /// <summary>Byte offset of the count inside the input count buffer.</summary>
237 public int inputCountBufferByteOffset;
238 /// <summary>GPU buffer defining the size of the input list.</summary>
239 public ComputeBuffer inputCountBuffer;
240 /// <summary>The input list.</summary>
241 public GraphicsBuffer input;
242 /// <summary>Required runtime resources.</summary>
243 public SupportResources supportResources;
244 }
245
246 /// <summary>
247 /// Structure defining any required assets used by the GPU sort.
248 /// </summary>
249 public struct SystemResources
250 {
251 /// <summary>
252 /// The compute asset that defines all of the kernels for the GPU prefix sum.
253 /// </summary>
254 public ComputeShader computeAsset;
255
256 internal int kernelCalculateLevelDispatchArgsFromConst;
257 internal int kernelCalculateLevelDispatchArgsFromBuffer;
258 internal int kernelPrefixSumOnGroup;
259 internal int kernelPrefixSumOnGroupExclusive;
260 internal int kernelPrefixSumNextInput;
261 internal int kernelPrefixSumResolveParent;
262 internal int kernelPrefixSumResolveParentExclusive;
263
264 internal void LoadKernels()
265 {
266 if (computeAsset == null)
267 return;
268
269 kernelCalculateLevelDispatchArgsFromConst = computeAsset.FindKernel("MainCalculateLevelDispatchArgsFromConst");
270 kernelCalculateLevelDispatchArgsFromBuffer = computeAsset.FindKernel("MainCalculateLevelDispatchArgsFromBuffer");
271 kernelPrefixSumOnGroup = computeAsset.FindKernel("MainPrefixSumOnGroup");
272 kernelPrefixSumOnGroupExclusive = computeAsset.FindKernel("MainPrefixSumOnGroupExclusive");
273 kernelPrefixSumNextInput = computeAsset.FindKernel("MainPrefixSumNextInput");
274 kernelPrefixSumResolveParent = computeAsset.FindKernel("MainPrefixSumResolveParent");
275 kernelPrefixSumResolveParentExclusive = computeAsset.FindKernel("MainPrefixSumResolveParentExclusive");
276 }
277 }
278 }
279}