A game about forced loneliness, made by TACStudios
at master 387 lines 18 kB view raw
1using System; 2using UnityEngine.Assertions; 3using Unity.Collections; 4using Unity.Jobs; 5using Unity.Collections.LowLevel.Unsafe; 6using Unity.Burst; 7using Unity.Mathematics; 8 9namespace UnityEngine.Rendering 10{ 11 internal unsafe struct LODGroupData 12 { 13 public const int k_MaxLODLevelsCount = 8; 14 15 public bool valid; 16 public int lodCount; 17 public int rendererCount; 18 public fixed float screenRelativeTransitionHeights[k_MaxLODLevelsCount]; 19 public fixed float fadeTransitionWidth[k_MaxLODLevelsCount]; 20 } 21 22 internal unsafe struct LODGroupCullingData 23 { 24 public float3 worldSpaceReferencePoint; 25 public int lodCount; 26 public fixed float sqrDistances[LODGroupData.k_MaxLODLevelsCount]; // we use square distance to get rid of a sqrt in gpu culling.. 27 public fixed float transitionDistances[LODGroupData.k_MaxLODLevelsCount]; // todo - make this a separate data struct (CPUOnly, as we do not support dithering on GPU..) 28 public float worldSpaceSize;// SpeedTree crossfade. 29 public fixed bool percentageFlags[LODGroupData.k_MaxLODLevelsCount];// SpeedTree crossfade. 30 } 31 32 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 33 internal struct UpdateLODGroupTransformJob : IJobParallelFor 34 { 35 public const int k_BatchSize = 256; 36 37 [ReadOnly] public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataHash; 38 [ReadOnly] public NativeArray<int> lodGroupIDs; 39 [ReadOnly] public NativeArray<Vector3> worldSpaceReferencePoints; 40 [ReadOnly] public NativeArray<float> worldSpaceSizes; 41 [ReadOnly] public bool requiresGPUUpload; 42 [ReadOnly] public bool supportDitheringCrossFade; 43 44 [NativeDisableContainerSafetyRestriction, NoAlias, ReadOnly] public NativeList<LODGroupData> lodGroupData; 45 46 [NativeDisableContainerSafetyRestriction, NoAlias, WriteOnly] public NativeList<LODGroupCullingData> lodGroupCullingData; 47 48 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateCount; 49 50 public unsafe void Execute(int index) 51 { 52 int lodGroupID = lodGroupIDs[index]; 53 54 if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) 55 { 56 var worldSpaceSize = worldSpaceSizes[index]; 57 58 LODGroupData* lodGroup = (LODGroupData*)lodGroupData.GetUnsafePtr() + lodGroupInstance.index; 59 LODGroupCullingData* lodGroupTransformResult = (LODGroupCullingData*)lodGroupCullingData.GetUnsafePtr() + lodGroupInstance.index; 60 lodGroupTransformResult->worldSpaceSize = worldSpaceSize; 61 lodGroupTransformResult->worldSpaceReferencePoint = worldSpaceReferencePoints[index]; 62 63 for (int i = 0; i < lodGroup->lodCount; ++i) 64 { 65 float lodHeight = lodGroup->screenRelativeTransitionHeights[i]; 66 67 var lodDist = LODGroupRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); 68 lodGroupTransformResult->sqrDistances[i] = lodDist * lodDist; 69 70 if (supportDitheringCrossFade && !lodGroupTransformResult->percentageFlags[i]) 71 { 72 float prevLODHeight = i != 0 ? lodGroup->screenRelativeTransitionHeights[i - 1] : 1.0f; 73 float transitionHeight = lodHeight + lodGroup->fadeTransitionWidth[i] * (prevLODHeight - lodHeight); 74 var transitionDistance = lodDist - LODGroupRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); 75 transitionDistance = Mathf.Max(0.0f, transitionDistance); 76 lodGroupTransformResult->transitionDistances[i] = transitionDistance; 77 } 78 else 79 { 80 lodGroupTransformResult->transitionDistances[i] = 0f; 81 } 82 83 } 84 } 85 } 86 } 87 88 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 89 internal unsafe struct AllocateOrGetLODGroupDataInstancesJob : IJob 90 { 91 [ReadOnly] public NativeArray<int> lodGroupsID; 92 93 public NativeList<LODGroupData> lodGroupsData; 94 public NativeList<LODGroupCullingData> lodGroupCullingData; 95 public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataHash; 96 public NativeList<GPUInstanceIndex> freeLODGroupDataHandles; 97 98 [WriteOnly] public NativeArray<GPUInstanceIndex> lodGroupInstances; 99 100 [NativeDisableUnsafePtrRestriction] public int* previousRendererCount; 101 102 public void Execute() 103 { 104 int freeHandlesCount = freeLODGroupDataHandles.Length; 105 int lodDataLength = lodGroupsData.Length; 106 107 for (int i = 0; i < lodGroupsID.Length; ++i) 108 { 109 int lodGroupID = lodGroupsID[i]; 110 111 if (!lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) 112 { 113 if (freeHandlesCount == 0) 114 lodGroupInstance = new GPUInstanceIndex() { index = lodDataLength++ }; 115 else 116 lodGroupInstance = freeLODGroupDataHandles[--freeHandlesCount]; 117 118 lodGroupDataHash.TryAdd(lodGroupID, lodGroupInstance); 119 } 120 else 121 { 122 *previousRendererCount += lodGroupsData.ElementAt(lodGroupInstance.index).rendererCount; 123 } 124 125 lodGroupInstances[i] = lodGroupInstance; 126 } 127 128 freeLODGroupDataHandles.ResizeUninitialized(freeHandlesCount); 129 lodGroupsData.ResizeUninitialized(lodDataLength); 130 lodGroupCullingData.ResizeUninitialized(lodDataLength); 131 } 132 } 133 134 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 135 internal unsafe struct UpdateLODGroupDataJob : IJobParallelFor 136 { 137 public const int k_BatchSize = 256; 138 139 [ReadOnly] public NativeArray<GPUInstanceIndex> lodGroupInstances; 140 [ReadOnly] public GPUDrivenLODGroupData inputData; 141 [ReadOnly] public bool supportDitheringCrossFade; 142 143 public NativeArray<LODGroupData> lodGroupsData; 144 public NativeArray<LODGroupCullingData> lodGroupsCullingData; 145 146 [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 rendererCount; 147 148 public void Execute(int index) 149 { 150 var lodGroupInstance = lodGroupInstances[index]; 151 var fadeMode = inputData.fadeMode[index]; 152 var lodOffset = inputData.lodOffset[index]; 153 var lodCount = inputData.lodCount[index]; 154 var renderersCount = inputData.renderersCount[index]; 155 var worldReferencePoint = inputData.worldSpaceReferencePoint[index]; 156 var worldSpaceSize = inputData.worldSpaceSize[index]; 157 var lastLODIsBillboard = inputData.lastLODIsBillboard[index]; 158 var useDitheringCrossFade = fadeMode != LODFadeMode.None && supportDitheringCrossFade; 159 var useSpeedTreeCrossFade = fadeMode == LODFadeMode.SpeedTree; 160 161 LODGroupData* lodGroupData = (LODGroupData*)lodGroupsData.GetUnsafePtr() + lodGroupInstance.index; 162 LODGroupCullingData* lodGroupCullingData = (LODGroupCullingData*)lodGroupsCullingData.GetUnsafePtr() + lodGroupInstance.index; 163 164 lodGroupData->valid = true; 165 lodGroupData->lodCount = lodCount; 166 lodGroupData->rendererCount = useDitheringCrossFade ? renderersCount : 0; 167 lodGroupCullingData->worldSpaceSize = worldSpaceSize; 168 lodGroupCullingData->worldSpaceReferencePoint = worldReferencePoint; 169 lodGroupCullingData->lodCount = lodCount; 170 171 rendererCount.Add(lodGroupData->rendererCount); 172 173 var crossFadeLODBegin = 0; 174 175 if (useSpeedTreeCrossFade) 176 { 177 var lastLODIndex = lodOffset + (lodCount - 1); 178 var hasBillboardLOD = lodCount > 0 && inputData.lodRenderersCount[lastLODIndex] == 1 && lastLODIsBillboard; 179 180 if (lodCount == 0) 181 crossFadeLODBegin = 0; 182 else if (hasBillboardLOD) 183 crossFadeLODBegin = Math.Max(lodCount, 2) - 2; 184 else 185 crossFadeLODBegin = lodCount - 1; 186 } 187 188 for (int i = 0; i < lodCount; ++i) 189 { 190 var lodIndex = lodOffset + i; 191 var lodHeight = inputData.lodScreenRelativeTransitionHeight[lodIndex]; 192 var lodDist = LODGroupRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); 193 194 lodGroupData->screenRelativeTransitionHeights[i] = lodHeight; 195 lodGroupData->fadeTransitionWidth[i] = 0.0f; 196 lodGroupCullingData->sqrDistances[i] = lodDist * lodDist; 197 lodGroupCullingData->percentageFlags[i] = false; 198 lodGroupCullingData->transitionDistances[i] = 0.0f; 199 200 if (useSpeedTreeCrossFade && i < crossFadeLODBegin) 201 { 202 lodGroupCullingData->percentageFlags[i] = true; 203 } 204 else if (useDitheringCrossFade && i >= crossFadeLODBegin) 205 { 206 var fadeTransitionWidth = inputData.lodFadeTransitionWidth[lodIndex]; 207 var prevLODHeight = i != 0 ? inputData.lodScreenRelativeTransitionHeight[lodIndex - 1] : 1.0f; 208 var transitionHeight = lodHeight + fadeTransitionWidth * (prevLODHeight - lodHeight); 209 var transitionDistance = lodDist - LODGroupRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); 210 transitionDistance = Mathf.Max(0.0f, transitionDistance); 211 212 lodGroupData->fadeTransitionWidth[i] = fadeTransitionWidth; 213 lodGroupCullingData->transitionDistances[i] = transitionDistance; 214 } 215 } 216 } 217 } 218 219 [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] 220 internal unsafe struct FreeLODGroupDataJob : IJob 221 { 222 [ReadOnly] public NativeArray<int> destroyedLODGroupsID; 223 224 public NativeList<LODGroupData> lodGroupsData; 225 public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataHash; 226 public NativeList<GPUInstanceIndex> freeLODGroupDataHandles; 227 228 [NativeDisableUnsafePtrRestriction] public int* removedRendererCount; 229 230 public void Execute() 231 { 232 foreach (int lodGroupID in destroyedLODGroupsID) 233 { 234 if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) 235 { 236 Assert.IsTrue(lodGroupInstance.valid); 237 238 lodGroupDataHash.Remove(lodGroupID); 239 freeLODGroupDataHandles.Add(lodGroupInstance); 240 241 ref LODGroupData lodGroupData = ref lodGroupsData.ElementAt(lodGroupInstance.index); 242 Assert.IsTrue(lodGroupData.valid); 243 244 *removedRendererCount += lodGroupData.rendererCount; 245 lodGroupData.valid = false; 246 } 247 } 248 } 249 } 250 251 internal class LODGroupDataPool : IDisposable 252 { 253 private NativeList<LODGroupData> m_LODGroupData; 254 private NativeParallelHashMap<int, GPUInstanceIndex> m_LODGroupDataHash; 255 public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataHash => m_LODGroupDataHash; 256 257 private NativeList<LODGroupCullingData> m_LODGroupCullingData; 258 private NativeList<GPUInstanceIndex> m_FreeLODGroupDataHandles; 259 260 private int m_CrossfadedRendererCount; 261 private bool m_SupportDitheringCrossFade; 262 263 public NativeList<LODGroupCullingData> lodGroupCullingData => m_LODGroupCullingData; 264 public int crossfadedRendererCount => m_CrossfadedRendererCount; 265 266 public int activeLodGroupCount => m_LODGroupData.Length; 267 268 private static class LodGroupShaderIDs 269 { 270 public static readonly int _SupportDitheringCrossFade = Shader.PropertyToID("_SupportDitheringCrossFade"); 271 public static readonly int _LodGroupCullingDataGPUByteSize = Shader.PropertyToID("_LodGroupCullingDataGPUByteSize"); 272 public static readonly int _LodGroupCullingDataStartOffset = Shader.PropertyToID("_LodGroupCullingDataStartOffset"); 273 public static readonly int _LodCullingDataQueueCount = Shader.PropertyToID("_LodCullingDataQueueCount"); 274 public static readonly int _InputLodCullingDataIndices = Shader.PropertyToID("_InputLodCullingDataIndices"); 275 public static readonly int _InputLodCullingDataBuffer = Shader.PropertyToID("_InputLodCullingDataBuffer"); 276 public static readonly int _LodGroupCullingData = Shader.PropertyToID("_LodGroupCullingData"); 277 } 278 279 public LODGroupDataPool(GPUResidentDrawerResources resources, int initialInstanceCount, bool supportDitheringCrossFade) 280 { 281 m_LODGroupData = new NativeList<LODGroupData>(Allocator.Persistent); 282 m_LODGroupDataHash = new NativeParallelHashMap<int, GPUInstanceIndex>(64, Allocator.Persistent); 283 284 m_LODGroupCullingData = new NativeList<LODGroupCullingData>(Allocator.Persistent); 285 m_FreeLODGroupDataHandles = new NativeList<GPUInstanceIndex>(Allocator.Persistent); 286 287 m_SupportDitheringCrossFade = supportDitheringCrossFade; 288 } 289 290 public void Dispose() 291 { 292 m_LODGroupData.Dispose(); 293 m_LODGroupDataHash.Dispose(); 294 295 m_LODGroupCullingData.Dispose(); 296 m_FreeLODGroupDataHandles.Dispose(); 297 } 298 299 public unsafe void UpdateLODGroupTransformData(in GPUDrivenLODGroupData inputData) 300 { 301 var lodGroupCount = inputData.lodGroupID.Length; 302 303 var updateCount = 0; 304 305 var jobData = new UpdateLODGroupTransformJob() 306 { 307 lodGroupDataHash = m_LODGroupDataHash, 308 lodGroupIDs = inputData.lodGroupID, 309 worldSpaceReferencePoints = inputData.worldSpaceReferencePoint, 310 worldSpaceSizes = inputData.worldSpaceSize, 311 lodGroupData = m_LODGroupData, 312 lodGroupCullingData = m_LODGroupCullingData, 313 supportDitheringCrossFade = m_SupportDitheringCrossFade, 314 atomicUpdateCount = new UnsafeAtomicCounter32(&updateCount), 315 }; 316 317 if (lodGroupCount >= UpdateLODGroupTransformJob.k_BatchSize) 318 jobData.Schedule(lodGroupCount, UpdateLODGroupTransformJob.k_BatchSize).Complete(); 319 else 320 jobData.Run(lodGroupCount); 321 } 322 323 public unsafe void UpdateLODGroupData(in GPUDrivenLODGroupData inputData) 324 { 325 FreeLODGroupData(inputData.invalidLODGroupID); 326 327 var lodGroupInstances = new NativeArray<GPUInstanceIndex>(inputData.lodGroupID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); 328 329 int previousRendererCount = 0; 330 331 new AllocateOrGetLODGroupDataInstancesJob 332 { 333 lodGroupsID = inputData.lodGroupID, 334 lodGroupsData = m_LODGroupData, 335 lodGroupCullingData = m_LODGroupCullingData, 336 lodGroupDataHash = m_LODGroupDataHash, 337 freeLODGroupDataHandles = m_FreeLODGroupDataHandles, 338 lodGroupInstances = lodGroupInstances, 339 previousRendererCount = &previousRendererCount 340 }.Run(); 341 342 m_CrossfadedRendererCount -= previousRendererCount; 343 Assert.IsTrue(m_CrossfadedRendererCount >= 0); 344 345 int rendererCount = 0; 346 347 var updateLODGroupDataJobData = new UpdateLODGroupDataJob 348 { 349 lodGroupInstances = lodGroupInstances, 350 inputData = inputData, 351 supportDitheringCrossFade = m_SupportDitheringCrossFade, 352 lodGroupsData = m_LODGroupData.AsArray(), 353 lodGroupsCullingData = m_LODGroupCullingData.AsArray(), 354 rendererCount = new UnsafeAtomicCounter32(&rendererCount), 355 }; 356 357 if (lodGroupInstances.Length >= UpdateLODGroupTransformJob.k_BatchSize) 358 updateLODGroupDataJobData.Schedule(lodGroupInstances.Length, UpdateLODGroupTransformJob.k_BatchSize).Complete(); 359 else 360 updateLODGroupDataJobData.Run(lodGroupInstances.Length); 361 362 m_CrossfadedRendererCount += rendererCount; 363 364 lodGroupInstances.Dispose(); 365 } 366 367 public unsafe void FreeLODGroupData(NativeArray<int> destroyedLODGroupsID) 368 { 369 if (destroyedLODGroupsID.Length == 0) 370 return; 371 372 int removedRendererCount = 0; 373 374 new FreeLODGroupDataJob 375 { 376 destroyedLODGroupsID = destroyedLODGroupsID, 377 lodGroupsData = m_LODGroupData, 378 lodGroupDataHash = m_LODGroupDataHash, 379 freeLODGroupDataHandles = m_FreeLODGroupDataHandles, 380 removedRendererCount = &removedRendererCount 381 }.Run(); 382 383 m_CrossfadedRendererCount -= removedRendererCount; 384 Assert.IsTrue(m_CrossfadedRendererCount >= 0); 385 } 386 } 387}