-
Notifications
You must be signed in to change notification settings - Fork 869
Expand file tree
/
Copy pathProbeGIBaking.SkyOcclusion.cs
More file actions
351 lines (277 loc) · 16.1 KB
/
ProbeGIBaking.SkyOcclusion.cs
File metadata and controls
351 lines (277 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
using System;
using System.Runtime.InteropServices;
using UnityEngine.Rendering.Sampling;
using UnityEngine.Rendering.UnifiedRayTracing;
using Unity.Collections;
namespace UnityEngine.Rendering
{
partial class AdaptiveProbeVolumes
{
/// <summary>
/// Sky occlusion baker
/// </summary>
public abstract class SkyOcclusionBaker : IDisposable
{
/// <summary>The current baking step.</summary>
public abstract ulong currentStep { get; }
/// <summary>The total amount of step.</summary>
public abstract ulong stepCount { get; }
/// <summary>Array storing the sky occlusion per probe. Expects Layout DC, x, y, z.</summary>
public abstract NativeArray<Vector4> occlusion { get; }
/// <summary>Array storing the sky shading direction per probe.</summary>
public abstract NativeArray<Vector3> shadingDirections { get; }
/// <summary>
/// This is called before the start of baking to allow allocating necessary resources.
/// </summary>
/// <param name="bakingSet">The baking set that is currently baked.</param>
/// <param name="probePositions">The probe positions.</param>
public abstract void Initialize(ProbeVolumeBakingSet bakingSet, NativeArray<Vector3> probePositions);
/// <summary>
/// Run a step of sky occlusion baking. Baking is considered done when currentStep property equals stepCount.
/// </summary>
/// <returns>Return false if bake failed and should be stopped.</returns>
public abstract bool Step();
/// <summary>
/// Performs necessary tasks to free allocated resources.
/// </summary>
public abstract void Dispose();
internal NativeArray<uint> encodedDirections;
internal void Encode() { encodedDirections = EncodeShadingDirection(shadingDirections); }
static int k_MaxProbeCountPerBatch = 65535;
static readonly int _SkyShadingPrecomputedDirection = Shader.PropertyToID("_SkyShadingPrecomputedDirection");
static readonly int _SkyShadingDirections = Shader.PropertyToID("_SkyShadingDirections");
static readonly int _SkyShadingIndices = Shader.PropertyToID("_SkyShadingIndices");
static readonly int _ProbeCount = Shader.PropertyToID("_ProbeCount");
internal static NativeArray<uint> EncodeShadingDirection(NativeArray<Vector3> directions)
{
var cs = GraphicsSettings.GetRenderPipelineSettings<ProbeVolumeBakingResources>().skyOcclusionCS;
int kernel = cs.FindKernel("EncodeShadingDirection");
ProbeVolumeConstantRuntimeResources.Initialize();
var precomputedShadingDirections = ProbeReferenceVolume.instance.GetRuntimeResources().SkyPrecomputedDirections;
int probeCount = directions.Length;
int batchSize = Mathf.Min(k_MaxProbeCountPerBatch, probeCount);
int batchCount = CoreUtils.DivRoundUp(probeCount, k_MaxProbeCountPerBatch);
var directionBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf<Vector3>());
var encodedBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf<uint>());
var directionResults = new NativeArray<uint>(probeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
for (int batchIndex = 0; batchIndex < batchCount; batchIndex++)
{
int batchOffset = batchIndex * k_MaxProbeCountPerBatch;
int probeInBatch = Mathf.Min(probeCount - batchOffset, k_MaxProbeCountPerBatch);
directionBuffer.SetData(directions, batchOffset, 0, probeInBatch);
cs.SetBuffer(kernel, _SkyShadingPrecomputedDirection, precomputedShadingDirections);
cs.SetBuffer(kernel, _SkyShadingDirections, directionBuffer);
cs.SetBuffer(kernel, _SkyShadingIndices, encodedBuffer);
cs.SetInt(_ProbeCount, probeInBatch);
cs.Dispatch(kernel, CoreUtils.DivRoundUp(probeCount, 64), 1, 1);
var batchResult = directionResults.GetSubArray(batchOffset, probeInBatch);
AsyncGPUReadback.RequestIntoNativeArray(ref batchResult, encodedBuffer, probeInBatch * sizeof(uint), 0).WaitForCompletion();
}
directionBuffer.Dispose();
encodedBuffer.Dispose();
return directionResults;
}
internal static uint EncodeSkyShadingDirection(Vector3 direction)
{
var precomputedDirections = ProbeVolumeConstantRuntimeResources.GetSkySamplingDirections();
uint indexMax = 255;
float bestDot = -10.0f;
uint bestIndex = 0;
for (uint index = 0; index < indexMax; index++)
{
float currentDot = Vector3.Dot(direction, precomputedDirections[index]);
if (currentDot > bestDot)
{
bestDot = currentDot;
bestIndex = index;
}
}
return bestIndex;
}
}
class DefaultSkyOcclusion : SkyOcclusionBaker
{
const int k_MaxProbeCountPerBatch = 128 * 1024;
const float k_SkyOcclusionOffsetRay = 0.015f;
const int k_SampleCountPerStep = 16;
static readonly int _SampleCount = Shader.PropertyToID("_SampleCount");
static readonly int _SampleId = Shader.PropertyToID("_SampleId");
static readonly int _MaxBounces = Shader.PropertyToID("_MaxBounces");
static readonly int _OffsetRay = Shader.PropertyToID("_OffsetRay");
static readonly int _ProbePositions = Shader.PropertyToID("_ProbePositions");
static readonly int _SkyOcclusionOut = Shader.PropertyToID("_SkyOcclusionOut");
static readonly int _SkyShadingOut = Shader.PropertyToID("_SkyShadingOut");
static readonly int _AverageAlbedo = Shader.PropertyToID("_AverageAlbedo");
static readonly int _BackFaceCulling = Shader.PropertyToID("_BackFaceCulling");
static readonly int _BakeSkyShadingDirection = Shader.PropertyToID("_BakeSkyShadingDirection");
static readonly int _SobolBuffer = Shader.PropertyToID("_SobolMatricesBuffer");
int skyOcclusionBackFaceCulling;
float skyOcclusionAverageAlbedo;
int probeCount;
ulong step;
// Input data
NativeArray<Vector3> probePositions;
int currentJob;
int sampleIndex;
int batchIndex;
public BakeJob[] jobs;
// Output buffers
GraphicsBuffer occlusionOutputBuffer;
GraphicsBuffer shadingDirectionBuffer;
NativeArray<Vector4> occlusionResults;
NativeArray<Vector3> directionResults;
public override NativeArray<Vector4> occlusion => occlusionResults;
public override NativeArray<Vector3> shadingDirections => directionResults;
AccelStructAdapter m_AccelerationStructure;
GraphicsBuffer scratchBuffer;
GraphicsBuffer probePositionsBuffer;
GraphicsBuffer sobolBuffer;
public override ulong currentStep => step;
public override ulong stepCount => (ulong)probeCount;
public override void Initialize(ProbeVolumeBakingSet bakingSet, NativeArray<Vector3> positions)
{
skyOcclusionAverageAlbedo = bakingSet.skyOcclusionAverageAlbedo;
skyOcclusionBackFaceCulling = 0; // see PR #40707
currentJob = 0;
sampleIndex = 0;
batchIndex = 0;
step = 0;
probeCount = bakingSet.skyOcclusion ? positions.Length : 0;
probePositions = positions;
if (stepCount == 0)
return;
// Allocate array storing results
occlusionResults = new NativeArray<Vector4>(probeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
if (bakingSet.skyOcclusionShadingDirection)
directionResults = new NativeArray<Vector3>(probeCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
// Create acceleration structure
m_AccelerationStructure = BuildAccelerationStructure();
var skyOcclusionShader = s_TracingContext.shaderSO;
bool skyDirection = shadingDirections.IsCreated;
int batchSize = Mathf.Min(k_MaxProbeCountPerBatch, probeCount);
probePositionsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf<Vector3>());
occlusionOutputBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, batchSize, Marshal.SizeOf<Vector4>());
shadingDirectionBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, skyDirection ? batchSize : 1, Marshal.SizeOf<Vector3>());
scratchBuffer = RayTracingHelper.CreateScratchBufferForBuildAndDispatch(m_AccelerationStructure.GetAccelerationStructure(), skyOcclusionShader, (uint)batchSize, 1, 1);
var buildCmd = new CommandBuffer();
m_AccelerationStructure.Build(buildCmd, ref scratchBuffer);
Graphics.ExecuteCommandBuffer(buildCmd);
buildCmd.Dispose();
int sobolBufferSize = (int)(SobolData.SobolDims * SobolData.SobolSize);
sobolBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, sobolBufferSize, Marshal.SizeOf<uint>());
sobolBuffer.SetData(SobolData.SobolMatrices);
}
static AccelStructAdapter BuildAccelerationStructure()
{
var accelStruct = s_TracingContext.CreateAccelerationStructure();
var contributors = m_BakingBatch.contributors;
foreach (var renderer in contributors.renderers)
{
if (!s_TracingContext.TryGetMeshForAccelerationStructure(renderer.component, out var mesh))
continue;
if (renderer.component is SkinnedMeshRenderer)
continue;
int subMeshCount = mesh.subMeshCount;
var matIndices = GetMaterialIndices(renderer.component);
var perSubMeshMask = new uint[subMeshCount];
Array.Fill(perSubMeshMask, GetInstanceMask(renderer.component.shadowCastingMode));
Span<bool> perSubMeshOpaqueness = stackalloc bool[subMeshCount];
perSubMeshOpaqueness.Fill(true);
accelStruct.AddInstance(EntityId.ToULong(renderer.component.GetEntityId()), renderer.component, perSubMeshMask, matIndices, perSubMeshOpaqueness, 1);
}
foreach (var terrain in contributors.terrains)
{
uint mask = GetInstanceMask(terrain.component.shadowCastingMode);
accelStruct.AddInstance(EntityId.ToULong(terrain.component.GetEntityId()), terrain.component, new uint[1] { mask }, new uint[1] { 0 }, new bool[1] { true }, 1);
}
return accelStruct;
}
public override bool Step()
{
if (currentStep >= stepCount)
return true;
ref var job = ref jobs[currentJob];
if (job.probeCount == 0)
{
currentJob++;
return true;
}
var cmd = new CommandBuffer();
var skyOccShader = s_TracingContext.shaderSO;
// Divide the job into batches of 128k probes to reduce memory usage.
int batchCount = CoreUtils.DivRoundUp(job.probeCount, k_MaxProbeCountPerBatch);
int batchOffset = batchIndex * k_MaxProbeCountPerBatch;
int batchSize = Mathf.Min(job.probeCount - batchOffset, k_MaxProbeCountPerBatch);
if (sampleIndex == 0)
{
cmd.SetBufferData(probePositionsBuffer, probePositions.GetSubArray(job.startOffset + batchOffset, batchSize));
}
s_TracingContext.BindSamplingTextures(cmd);
m_AccelerationStructure.Bind(cmd, "_AccelStruct", skyOccShader);
skyOccShader.SetIntParam(cmd, _BakeSkyShadingDirection, shadingDirections.IsCreated ? 1 : 0);
skyOccShader.SetIntParam(cmd, _BackFaceCulling, skyOcclusionBackFaceCulling);
skyOccShader.SetFloatParam(cmd, _AverageAlbedo, skyOcclusionAverageAlbedo);
skyOccShader.SetFloatParam(cmd, _OffsetRay, k_SkyOcclusionOffsetRay);
skyOccShader.SetBufferParam(cmd, _ProbePositions, probePositionsBuffer);
skyOccShader.SetBufferParam(cmd, _SkyOcclusionOut, occlusionOutputBuffer);
skyOccShader.SetBufferParam(cmd, _SkyShadingOut, shadingDirectionBuffer);
skyOccShader.SetBufferParam(cmd, _SobolBuffer, sobolBuffer);
skyOccShader.SetIntParam(cmd, _SampleCount, job.skyOcclusionBakingSamples);
skyOccShader.SetIntParam(cmd, _MaxBounces, job.skyOcclusionBakingBounces);
// Sample multiple paths in one step
for (int i = 0; i < k_SampleCountPerStep; i++)
{
skyOccShader.SetIntParam(cmd, _SampleId, sampleIndex);
skyOccShader.Dispatch(cmd, scratchBuffer, (uint)batchSize, 1, 1);
sampleIndex++;
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
// If we computed all the samples for this batch, continue with the next one
if (sampleIndex >= job.skyOcclusionBakingSamples)
{
FetchResults(in job, batchOffset, batchSize);
batchIndex++;
sampleIndex = 0;
if (batchIndex >= batchCount)
{
currentJob++;
batchIndex = 0;
}
// Progress bar
step += (ulong)batchSize;
break;
}
}
cmd.Dispose();
return true;
}
void FetchResults(in BakeJob job, int batchOffset, int batchSize)
{
var batchOcclusionResults = occlusionResults.GetSubArray(job.startOffset + batchOffset, batchSize);
var req1 = AsyncGPUReadback.RequestIntoNativeArray(ref batchOcclusionResults, occlusionOutputBuffer, batchSize * 4 * sizeof(float), 0);
if (directionResults.IsCreated)
{
var batchDirectionResults = directionResults.GetSubArray(job.startOffset + batchOffset, batchSize);
var req2 = AsyncGPUReadback.RequestIntoNativeArray(ref batchDirectionResults, shadingDirectionBuffer, batchSize * 3 * sizeof(float), 0);
req2.WaitForCompletion();
}
// TODO: use double buffering to hide readback latency
req1.WaitForCompletion();
}
public override void Dispose()
{
if (m_AccelerationStructure == null)
return;
occlusionOutputBuffer?.Dispose();
shadingDirectionBuffer?.Dispose();
scratchBuffer?.Dispose();
probePositionsBuffer?.Dispose();
sobolBuffer?.Dispose();
occlusionResults.Dispose();
if (directionResults.IsCreated)
directionResults.Dispose();
m_AccelerationStructure.Dispose();
}
}
}
}