Material Clustering Benchmark
ClusteringRTsAndBuffers.cs
Go to the documentation of this file.
1using UnityEngine;
2using System;
3using static Diagnostics;
4
6{
7 /// <summary>
8 /// Call <see cref="Allocate" /> before using and <see cref="Dispose" /> after using.
9 /// </summary>
10 public class ClusteringRTsAndBuffers : System.IDisposable
11 {
12 public const int max_num_clusters = 32;
13 public const bool randomInit = true;
14
15 public RenderTexture rtResult;
16 public ClusteringTextures texturesWorkRes { get; private set; }
17 public ClusteringTextures texturesFullRes { get; private set; }
18 public ComputeBuffer cbufClusterCenters { get; private set; }
19 public ComputeBuffer cbufRandomPositions { get; private set; }
20 public readonly int numClusters;
21 public readonly int jitterSize;
22
23 private readonly int[] scanlinePixelOffset = new int[2];
24 private int[][] jitterOffsets;
25
26 /// <summary>
27 /// Get a pooled instance of ClusterCenters with a copy of the data from ComputeBuffer. Safe to modify.<para />
28 /// Don't forget to dispose.
29 /// </summary>
30 /// <returns></returns>
32 {
33 this.cbufClusterCenters.GetData(this.clusterCentersTempData);
34 return ClusterCenters.Get(this.numClusters, this.clusterCentersTempData);
35 }
36
37 /// <summary>
38 /// Reads the cluster centers data from the array and loads it into the ComputeBuffer.
39 /// </summary>
40 /// <param name="clusterCentersBufferData"></param>
41 public void SetClusterCenters(Vector4[] clusterCentersBufferData)
42 {
43 this.cbufClusterCenters.SetData(clusterCentersBufferData);
44 }
45
46 private Vector4[] clusterCentersTempData;
47
48 /// <summary>
49 /// Integer coordinates for working resolution texture.<para />
50 /// These positions are used for <see cref="ADispatcherRS" /> (random swap) and for randomizing empty clusters <see cref="ADispatcher.doRandomizeEmptyClusters" />.
51 /// </summary>
52 private Position[] randomPositions;
53 private System.Random random;
54
55 private struct Position
56 {
57 public int x;
58 public int y;
59
60 /*
61 NVidia says structures not aligned to 128 bits are slow
62 https://developer.nvidia.com/content/understanding-structured-buffer-performance
63 */
64 private readonly int padding_1;
65 private readonly int padding_2;
66 }
67
69 {
70 return this.random.Next(numClusters);
71 }
72
74 {
75 int textureSize = this.texturesWorkRes.size;
76
77 for (int k = 0; k < this.randomPositions.Length; k++)
78 {
79 this.randomPositions[k].x = this.random.Next(textureSize);
80 this.randomPositions[k].y = this.random.Next(textureSize);
81 }
82 this.cbufRandomPositions.SetData(this.randomPositions);
83 }
84
85 public readonly int workingSize;
86 public readonly int fullSize;
87
88 /// <summary>
89 /// Textures are not allocated in the constructor. Call <see cref="Allocate" /> manually before using.
90 /// </summary>
92 int numClusters,
93 int workingSize,
94 int fullSize,
95 int jitterSize
96 )
97 {
98 this.numClusters = numClusters;
99 this.workingSize = workingSize;
100 this.fullSize = fullSize;
101 this.jitterSize = jitterSize;
102 }
103
104 public void Allocate()
105 {
106 Assert(
107 this.isAllocated == false,
108 $"Attempting to allocate already allocated {typeof(ClusteringRTsAndBuffers)}"
109 );
110
111 this.rtResult = new RenderTexture(
112 this.workingSize,
113 this.workingSize,
114 0,
115 RenderTextureFormat.ARGBFloat
116 )
117 {
118 enableRandomWrite = true,
119 filterMode = FilterMode.Point
120 };
121
122 this.jitterOffsets = JitterPattern.Get(this.jitterSize);
123
124 this.texturesWorkRes = new ClusteringTextures(this.workingSize);
125 this.texturesFullRes = new ClusteringTextures(this.fullSize);
126
127 this.random = new System.Random();
128
129 this.cbufRandomPositions = new ComputeBuffer(max_num_clusters, sizeof(int) * 4);
130 this.randomPositions = new Position[max_num_clusters];
132
133 /*
134 second half of the buffer contains candidate cluster centers
135 first half contains current cluster centers
136
137 NVidia says structures not aligned to 128 bits are slow
138 https://developer.nvidia.com/content/understanding-structured-buffer-performance
139 */
140 this.cbufClusterCenters = new ComputeBuffer(this.numClusters * 2, sizeof(float) * 4);
141 this.clusterCentersTempData = new Vector4[this.numClusters * 2];
142#pragma warning disable 162
143 if (randomInit)
144 {
146 }
147 else
148 {
150 }
151#pragma warning restore 162
152 }
153
154 public bool isAllocated => this.cbufClusterCenters != null;
155
156 private Vector3 RGB2KKK(Vector3 rgb)
157 {
158 var result =
159 new Matrix4x4(
160 new Vector4(1.0f / 3, 1.0f / 2, -1.0f / 4, 0.0f),
161 new Vector4(1.0f / 3, 0.0f, 1.0f / 2, 0.0f),
162 new Vector4(1.0f / 3, -1.0f / 2, -1.0f / 4, 0.0f),
163 Vector4.zero
164 ) * new Vector4(rgb.x, rgb.y, rgb.z, 0);
165 return result;
166 }
167
169 {
170 for (int i = 0; i < this.numClusters; i++)
171 {
172 var c = Color.HSVToRGB(i / (float)(this.numClusters), 1, 1);
173
174 Vector3 kkkColor = RGB2KKK(new Vector4(c.r, c.g, c.b, 0));
175
176 // variance infinity to ensure new cluster centers will replace these ones
177 this.clusterCentersTempData[i] = new Vector4(
178 // chromaticity axes
179 kkkColor.y,
180 kkkColor.z,
181 // no valid variance
182 -1,
183 // empty cluster
184 0
185 ); // "new" cluster center
186 this.clusterCentersTempData[i + this.numClusters] = new Vector4(
187 // chromaticity axes
188 kkkColor.y,
189 kkkColor.z,
190 // no valid variance
191 -1,
192 // empty cluster
193 0
194 ); // "old" cluster center
195 }
196 this.cbufClusterCenters.SetData(this.clusterCentersTempData);
197 }
198
200 {
201 for (int i = 0; i < this.numClusters; i++)
202 {
203 var c = Color.HSVToRGB((float)this.random.NextDouble(), 1, 1);
204 Vector4 kkkColor = RGB2KKK(new Vector4(c.r, c.g, c.b, 0));
205
206 // variance infinity to ensure new cluster centers will replace these ones
207 this.clusterCentersTempData[i] = new Vector4(
208 // chromaticity axes
209 kkkColor.y,
210 kkkColor.z,
211 // no valid variance
212 -1,
213 // empty cluster
214 0
215 ); // "new" cluster center
216 this.clusterCentersTempData[i + this.numClusters] = new Vector4(
217 // chromaticity axes
218 kkkColor.y,
219 kkkColor.z,
220 // no valid variance
221 -1,
222 // empty cluster
223 0
224 ); // "old" cluster center
225 }
226 this.cbufClusterCenters.SetData(this.clusterCentersTempData);
227 }
228
229 /// <summary>
230 /// Update textureWorkRes by downsampling textureFullRes
231 /// </summary>
232 public void Downsample(
233 ComputeShader csHighlightRemoval,
234 bool staggeredJitter,
235 bool doDownscale
236 )
237 {
238 int kernelSubsample = csHighlightRemoval.FindKernel("SubSample");
239
240 csHighlightRemoval.SetInt(
241 "sub_sample_multiplier",
242 this.texturesFullRes.size / this.texturesWorkRes.size
243 );
244
245 csHighlightRemoval.SetInt(
246 "subsample_mip_level",
247 this.texturesFullRes.mipLevel - this.texturesWorkRes.mipLevel
248 );
249
250 if (staggeredJitter)
251 {
252 csHighlightRemoval.SetInts(
253 "sub_sample_offset",
254 this.jitterOffsets[Time.frameCount % this.jitterOffsets.Length]
255 );
256 }
257 else
258 {
259 this.scanlinePixelOffset[0] = Time.frameCount % this.jitterSize;
260 this.scanlinePixelOffset[1] =
261 (Time.frameCount / this.jitterOffsets.Length) % this.jitterSize;
262 csHighlightRemoval.SetInts("sub_sample_offset", this.scanlinePixelOffset);
263 }
264
265 csHighlightRemoval.SetTexture(
266 kernelSubsample,
267 "tex_input",
269 );
270
271 csHighlightRemoval.SetTexture(
272 kernelSubsample,
273 "tex_output",
275 );
276
277 csHighlightRemoval.SetBool("downscale", doDownscale);
278
279 csHighlightRemoval.Dispatch(
280 kernelSubsample,
281 Math.Max(this.texturesWorkRes.size / ClusteringTest.kernelSize, 1),
282 Math.Max(this.texturesWorkRes.size / ClusteringTest.kernelSize, 1),
283 1
284 );
285 }
286
287 public void Dispose()
288 {
289 this.cbufClusterCenters.Release();
290 this.cbufRandomPositions.Release();
291 this.rtResult.Release();
294 }
295 }
296}
Call Dispose after using.
static ClusterCenters Get(int numClusters, Vector4[] centersBufferData)
Gets a pooled instance of ClusterCenters. The data is copied, making is safe to edit....
Call Allocate before using and Dispose after using.
ClusteringRTsAndBuffers(int numClusters, int workingSize, int fullSize, int jitterSize)
Textures are not allocated in the constructor. Call Allocate manually before using.
void Downsample(ComputeShader csHighlightRemoval, bool staggeredJitter, bool doDownscale)
Update textureWorkRes by downsampling textureFullRes
ClusterCenters GetClusterCenters()
Get a pooled instance of ClusterCenters with a copy of the data from ComputeBuffer....
void SetClusterCenters(Vector4[] clusterCentersBufferData)
Reads the cluster centers data from the array and loads it into the ComputeBuffer.
const int kernelSize
Must match the shader.
Call Dispose after using.
readonly RenderTexture rtInput
static int[][] Get(int size)
Definition: JitterPattern.cs:5