Browse Source

Merge branch 'ww1dev/hdrp17/svn' into ww1dev/hdrp17/staging

ww1dev/hdrp17/staging
Nico de Poel 6 months ago
parent
commit
8c56fe1834
  1. 2
      Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeStreamableAsset.cs
  2. 4
      Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Reflection/HDProbe.cs
  3. 2
      Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/PSSRUpscalerPlugin.cs
  4. 18
      Packages/com.unity.rendering.light-transport/CHANGELOG.md
  5. 7
      Packages/com.unity.rendering.light-transport/CHANGELOG.md.meta
  6. 8
      Packages/com.unity.rendering.light-transport/Editor.meta
  7. 78
      Packages/com.unity.rendering.light-transport/Editor/ShaderTemplates.cs
  8. 11
      Packages/com.unity.rendering.light-transport/Editor/ShaderTemplates.cs.meta
  9. 32
      Packages/com.unity.rendering.light-transport/Editor/UnifiedRTShaderImporter.cs
  10. 2
      Packages/com.unity.rendering.light-transport/Editor/UnifiedRTShaderImporter.cs.meta
  11. 16
      Packages/com.unity.rendering.light-transport/Editor/Unity.Rendering.LightTransport.Editor.asmdef
  12. 7
      Packages/com.unity.rendering.light-transport/Editor/Unity.Rendering.LightTransport.Editor.asmdef.meta
  13. 5
      Packages/com.unity.rendering.light-transport/LICENSE.md
  14. 7
      Packages/com.unity.rendering.light-transport/LICENSE.md.meta
  15. 8
      Packages/com.unity.rendering.light-transport/Runtime.meta
  16. 17
      Packages/com.unity.rendering.light-transport/Runtime/AssemblyInfo.cs
  17. 11
      Packages/com.unity.rendering.light-transport/Runtime/AssemblyInfo.cs.meta
  18. 8
      Packages/com.unity.rendering.light-transport/Runtime/Sampling.meta
  19. 128
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Common.hlsl
  20. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Common.hlsl.meta
  21. 143
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Hashes.hlsl
  22. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Hashes.hlsl.meta
  23. 268
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/QuasiRandom.hlsl
  24. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/QuasiRandom.hlsl.meta
  25. 70
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.cs
  26. 2
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.cs.meta
  27. 14
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.hlsl
  28. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.hlsl.meta
  29. 31
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolBluenoiseSampling.hlsl
  30. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolBluenoiseSampling.hlsl.meta
  31. 53288
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolData.cs
  32. 2
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolData.cs.meta
  33. 169
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolSampling.hlsl
  34. 7
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolSampling.hlsl.meta
  35. 8
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures.meta
  36. 8
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise.meta
  37. BIN
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/RankingTile256SPP.png
  38. 127
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/RankingTile256SPP.png.meta
  39. BIN
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/ScramblingTile256SPP.png
  40. 127
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/ScramblingTile256SPP.png.meta
  41. BIN
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/SobolOwenScrambled256.png
  42. 242
      Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/SobolOwenScrambled256.png.meta
  43. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing.meta
  44. 133
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Bindings.hlsl
  45. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Bindings.hlsl.meta
  46. 32
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.hlsl
  47. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.hlsl.meta
  48. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.meta
  49. 295
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs
  50. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs.meta
  51. 232
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructInstances.cs
  52. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructInstances.cs.meta
  53. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool.meta
  54. 869
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.cs
  55. 2
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.cs.meta
  56. 113
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.hlsl
  57. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.hlsl.meta
  58. 40
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs
  59. 53
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.hlsl
  60. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.hlsl.meta
  61. 2
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.meta
  62. 106
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolKernels.compute
  63. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolKernels.compute.meta
  64. 284
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs
  65. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs.meta
  66. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities.meta
  67. 29
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BackendHelpers.cs
  68. 2
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BackendHelpers.cs.meta
  69. 272
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BlockAllocator.cs
  70. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BlockAllocator.cs.meta
  71. 24
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/CopyBuffer.compute
  72. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/CopyBuffer.compute.meta
  73. 68
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/GraphicsHelpers.cs
  74. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/GraphicsHelpers.cs.meta
  75. 163
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/PersistentGPUArray.cs
  76. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/PersistentGPUArray.cs.meta
  77. 22
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/Utils.cs
  78. 2
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/Utils.cs.meta
  79. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute.meta
  80. 95
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/BLASPositionsPool.cs
  81. 2
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/BLASPositionsPool.cs.meta
  82. 695
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingAccelStruct.cs
  83. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingAccelStruct.cs.meta
  84. 29
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingBackend.cs
  85. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingBackend.cs.meta
  86. 123
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingShader.cs
  87. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingShader.cs.meta
  88. 36
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl
  89. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl.meta
  90. 8
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays.meta
  91. 312
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/BvhCheck.cs
  92. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/BvhCheck.cs.meta
  93. 23
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/Common.cs
  94. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/Common.cs.meta
  95. 162
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhBuilder.cs
  96. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhBuilder.cs.meta
  97. 187
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhTopLevelBuilder.cs
  98. 11
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhTopLevelBuilder.cs.meta
  99. 5
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/README.txt
  100. 7
      Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/README.txt.meta

2
Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeVolumeStreamableAsset.cs

@ -68,7 +68,7 @@ namespace UnityEngine.Rendering
internal bool HasValidAssetReference()
{
return m_Asset != null && m_Asset.bytes != null;
return m_Asset != null && m_Asset.dataSize > 64; //WW1MOD used to READ THE ENTIRE FILE just to check there is data
}
unsafe public bool FileExists()

4
Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/Reflection/HDProbe.cs

@ -935,6 +935,10 @@ namespace UnityEngine.Rendering.HighDefinition
void UpdateProbeName()
{
//WW1MOD: None of this naming is even really necessary... thanks
if (this == null)
return;
if (settings.type == ProbeSettings.ProbeType.ReflectionProbe)
{
for (int i = 0; i < 6; i++)

2
Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/Upscalers/PSSRUpscalerPlugin.cs

@ -201,6 +201,8 @@ namespace UnityEngine.Rendering.HighDefinition.AMD.PSSR
dispatchParams.camFar = executeData.cameraFar;
dispatchParams.preExposure = executeData.preExposure;
dispatchParams.resetHistory = (uint)executeData.reset;
dispatchParams.colorSpace = (_initData.ffxFsrFlags & FfxFsr2InitializationFlags.EnableHighDynamicRange) != 0 ? PSSRPlugin.ColorSpace.Rec2020 : PSSRPlugin.ColorSpace.Rec709;
dispatchParams.colorGamma = PSSRPlugin.ColorGamma.Linear;
dispatchParams.flags = flags;
PSSRPlugin.IssuePluginEvent(cmd, PSSRPlugin.Event.Dispatch, _dispatchParams);

18
Packages/com.unity.rendering.light-transport/CHANGELOG.md

@ -0,0 +1,18 @@
# Changelog
All notable changes to this package will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.0.1] - 2024-01-08
### Fixed
- Fixed UUM-59879: MissingComponentException when no MeshFilter is attached to a Terrain tree game object.
## [1.0.0] - 2023-04-25
### Added
- Initial package release

7
Packages/com.unity.rendering.light-transport/CHANGELOG.md.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f7b7504be10436f4195c66a61397c633
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Editor.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5cbcf068765868439bec8721c1e76b2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

78
Packages/com.unity.rendering.light-transport/Editor/ShaderTemplates.cs

@ -0,0 +1,78 @@
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.ProjectWindowCallback;
namespace UnityEditor.Rendering.UnifiedRayTracing
{
internal class ShaderTemplates
{
// TODO: Uncomment when API is made public
//[MenuItem("Assets/Create/Shader/Unified RayTracing Shader", false, 1)]
internal static void CreateNewUnifiedRayTracingShader()
{
var action = ScriptableObject.CreateInstance<DoCreateUnifiedRayTracingShaders>();
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, action, "NewRayTracingShader.hlsl", null, null);
}
internal static Object CreateScriptAssetWithContent(string pathName, string templateContent)
{
string fullPath = Path.GetFullPath(pathName);
File.WriteAllText(fullPath, templateContent);
AssetDatabase.ImportAsset(pathName);
return AssetDatabase.LoadAssetAtPath(pathName, typeof(Object));
}
internal class DoCreateUnifiedRayTracingShaders : EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
var includeName = Path.GetFileNameWithoutExtension(pathName);
Object o = CreateScriptAssetWithContent(pathName, shaderContent);
CreateScriptAssetWithContent(Path.ChangeExtension(pathName, ".compute"), computeShaderContent.Replace("SHADERNAME", includeName));
CreateScriptAssetWithContent(Path.ChangeExtension(pathName, ".raytrace"), raytracingShaderContent.Replace("SHADERNAME", includeName));
ProjectWindowUtil.ShowCreatedAsset(o);
}
}
const string computeShaderContent =
@"#define UNIFIED_RT_BACKEND_COMPUTE
#define UNIFIED_RT_GROUP_SIZE_X 16
#define UNIFIED_RT_GROUP_SIZE_Y 8
#include ""SHADERNAME.hlsl""
#include_with_pragmas ""Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl""
";
const string raytracingShaderContent =
@"#define UNIFIED_RT_BACKEND_HARDWARE
#include ""SHADERNAME.hlsl""
#include_with_pragmas ""Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Hardware/HardwareRaygenShader.hlsl""
";
const string shaderContent =
@"#include ""Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/FetchGeometry.hlsl""
#include ""Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/TraceRay.hlsl""
UNIFIED_RT_DECLARE_ACCEL_STRUCT(_AccelStruct);
void RayGenExecute(UnifiedRT::DispatchInfo dispatchInfo)
{
// Example code:
UnifiedRT::Ray ray;
ray.origin = 0;
ray.direction = float3(0, 0, 1);
ray.tMin = 0;
ray.tMax = 1000.0f;
UnifiedRT::RayTracingAccelStruct accelStruct = UNIFIED_RT_GET_ACCEL_STRUCT(_AccelStruct);
UnifiedRT::Hit hitResult = UnifiedRT::TraceRayClosestHit(dispatchInfo, accelStruct, 0xFFFFFFFF, ray, 0);
if (hitResult.IsValid())
{
UnifiedRT::HitGeomAttributes attributes = UnifiedRT::FetchHitGeomAttributes(accelStruct, hitResult);
}
}
";
}
}

11
Packages/com.unity.rendering.light-transport/Editor/ShaderTemplates.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a847ad517fa47d42acbda150212f026
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

32
Packages/com.unity.rendering.light-transport/Editor/UnifiedRTShaderImporter.cs

@ -0,0 +1,32 @@
using UnityEditor.AssetImporters;
using System.IO;
namespace UnityEditor.Rendering.UnifiedRayTracing
{
[ScriptedImporter(1, "urtshader")]
internal class UnifiedRTShaderImporter : ScriptedImporter
{
public override void OnImportAsset(AssetImportContext ctx)
{
string source = File.ReadAllText(ctx.assetPath);
var com = ShaderUtil.CreateComputeShaderAsset(ctx, computeShaderTemplate.Replace("SHADERCODE", source));
var rt = ShaderUtil.CreateRayTracingShaderAsset(ctx,
raytracingShaderTemplate.Replace("SHADERCODE", source));
ctx.AddObjectToAsset("ComputeShader", com);
ctx.AddObjectToAsset("RayTracingShader", rt);
ctx.SetMainObject(com);
}
const string computeShaderTemplate =
"#define UNIFIED_RT_BACKEND_COMPUTE\n" +
"SHADERCODE\n" +
"#include_with_pragmas \"Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl\"\n";
const string raytracingShaderTemplate =
"#define UNIFIED_RT_BACKEND_HARDWARE\n" +
"SHADERCODE\n" +
"#include_with_pragmas \"Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Hardware/HardwareRaygenShader.hlsl\"\n";
}
}

2
Packages/com.unity.rendering.light-transport/Editor/UnifiedRTShaderImporter.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6a2e376a7f79aa2439fd62f49ed5e1b6

16
Packages/com.unity.rendering.light-transport/Editor/Unity.Rendering.LightTransport.Editor.asmdef

@ -0,0 +1,16 @@
{
"name": "Unity.Rendering.LightTransport.Editor",
"rootNamespace": "",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

7
Packages/com.unity.rendering.light-transport/Editor/Unity.Rendering.LightTransport.Editor.asmdef.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 085cb502223da8f46931abfa47535112
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

5
Packages/com.unity.rendering.light-transport/LICENSE.md

@ -0,0 +1,5 @@
com.unity.rendering.light-transport copyright © 2023 Unity Technologies
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

7
Packages/com.unity.rendering.light-transport/LICENSE.md.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3f9ab173074b27e4d9dbb962d586e9ea
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9d005ee0c906d624e8dbc3baba73d1b6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

17
Packages/com.unity.rendering.light-transport/Runtime/AssemblyInfo.cs

@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
// Make visible to tests
[assembly: InternalsVisibleTo("Unity.PathTracing.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.PathTracing.Runtime.Tests")]
[assembly: InternalsVisibleTo("Unity.LightTransport.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Testing.Rendering.LightTransport.Runtime")]
[assembly: InternalsVisibleTo("Unity.Testing.Performance.LightTransport.Runtime")]
[assembly: InternalsVisibleTo("Assembly-CSharp-editor-testable")]
// Make visible internally to packages using the Unified Raytracing API, TODO: remove when the API is made public
[assembly: InternalsVisibleTo("Unity.PathTracing.Runtime")]
[assembly: InternalsVisibleTo("Unity.PathTracing.Editor")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.HighDefinition.Runtime")]
[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Runtime")]

11
Packages/com.unity.rendering.light-transport/Runtime/AssemblyInfo.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4ae04804288817e4eb46719b68239d1e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/Sampling.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eb3e9bc25e6bb0b4e9a52eba5158e661
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

128
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Common.hlsl

@ -0,0 +1,128 @@
#ifndef _SAMPLING_SAMPLING_COMMON_HLSL_
#define _SAMPLING_SAMPLING_COMMON_HLSL_
#ifndef PI
#define PI 3.141592653589f
#endif
// Paper: Building an Orthonormal Basis, Revisited.
// Tom Duff, James Burgess, Per Christensen, Christophe Hery, Andrew Kensler, Max Liani, and Ryusuke Villemin (Pixar).
// https://graphics.pixar.com/library/OrthonormalB/paper.pdf
void OrthoBasisFromVector(float3 n, out float3 b1, out float3 b2)
{
float sign = n.z >= 0.0f ? 1.0f : -1.0f;
const float a = -1.0f / (sign + n.z);
const float b = n.x * n.y * a;
b1 = float3(1.0f + sign * n.x * n.x * a, sign * b, -sign * n.x);
b2 = float3(b, sign + n.y * n.y * a, -n.y);
}
float3x3 OrthoBasisFromVector(float3 n)
{
float3 t, b;
OrthoBasisFromVector(n, t, b);
return transpose(float3x3(t, b, n));
}
void SampleDiffuseBrdf(float2 u, float3 shadingNormal, out float3 wi)
{
wi = float3(0, 0, 0);
float a = sqrt(u.x);
float b = 2.0 * PI * u.y;
float3 localWi = float3(a * cos(b), a * sin(b), sqrt(1.0f - u.x));
float3x3 TBN = OrthoBasisFromVector(shadingNormal);
wi = mul(TBN, localWi);
}
bool SampleDiffuseBrdf(float2 u, float3 geometryNormal, float3 shadingNormal, float3 wo, out float3 wi, out float pdf)
{
pdf = 0.0f;
wi = 0.0f;
bool valid = true;
if (dot(geometryNormal, wo) <= 0.0f)
valid = false;
else
{
float a = sqrt(1.0f - u.x);
float b = 2.0 * PI * u.y;
float3 localWi = float3(a * cos(b), a * sin(b), sqrt(u.x));
float3x3 TBN = OrthoBasisFromVector(shadingNormal);
wi = mul(TBN, localWi);
pdf = localWi.z / PI;
}
return valid;
}
float3 MapSquareToSphere(float2 unitSquareCoords)
{
float theta = (2.0f * PI) * unitSquareCoords.x;
float sinTheta, cosTheta;
sincos(theta, sinTheta, cosTheta);
float cosPhi = 1.0f - 2.0f * unitSquareCoords.y;
float sinPhi = sqrt(max(0.0f, 1.0f - cosPhi * cosPhi));
return float3(sinPhi * cosTheta, sinPhi * sinTheta, cosPhi);
}
// An optimized version of the area-preserving square-to-disk map presented in "A Low Distortion Map Between Disk and Square". Copied from from http://psgraphics.blogspot.com/2011/01/improved-code-for-concentric-map.html.
float2 MapSquareToDisk(float2 rnd)
{
//Code flow makes sure that division by 0 and thus NaNs cannot happen.
float phi;
float r;
float a = rnd.x * 2.0f - 1.0f;
float b = rnd.y * 2.0f - 1.0f;
if (a * a > b * b)
{
r = a;
phi = (PI * 0.25f) * (b / a);
}
else
{
r = b;
if (b == 0.0f)
phi = PI * 0.5f;
else
phi = (PI * 0.5f) - (PI * 0.25f) * (a / b);
}
return float2(r * cos(phi), r * sin(phi));
}
float3 CosineSample(float2 u, float3 normal)
{
float a = sqrt(u.x);
float b = 2.0f * PI * u.y;
float3 localDir = float3(a * cos(b), a * sin(b), sqrt(1.0f - u.x));
float3x3 basis = OrthoBasisFromVector(normal);
return mul(basis, localDir);
}
float PowerHeuristic(float f, float b)
{
float q = (f * f) + (b * b);
return q > 0 ? (f * f) / q : 0;
}
float UintToFloat01(uint x)
{
return x * 2.3283064365386963e-10; // (1.f / (1ULL << 32));
}
int Log2Int(uint v)
{
return firstbithigh(v);
}
int Log2IntUp(uint v)
{
int n = Log2Int(v);
return v > (1u << n) ? n + 1 : n;
}
#endif

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Common.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3439ab1cb0e22d54bb9ce1c5c676045e
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

143
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Hashes.hlsl

@ -0,0 +1,143 @@
#ifndef _SAMPLING_HASHES_HLSL_
#define _SAMPLING_HASHES_HLSL_
// Low bias hash from https://github.com/skeeto/hash-prospector
uint LowBiasHash32(uint x, uint seed = 0)
{
x += seed;
x ^= x >> 16;
x *= 0x21f0aaad;
x ^= x >> 15;
x *= 0xd35a2d97;
x ^= x >> 15;
return x;
}
// Murmur Hash from https://github.com/aappleby/smhasher/wiki/MurmurHash3
uint MurmurAdd(uint hash, uint item)
{
item *= 0xcc9e2d51;
item = (item << 15) | (item >> 17);
item *= 0x1b873593;
hash ^= item;
hash = (hash << 13) | (hash >> 19);
hash = hash * 5 + 0xe6546b64;
return hash;
}
uint MurmurFinalize(uint hash)
{
hash ^= hash >> 16;
hash *= 0x85ebca6b;
hash ^= hash >> 13;
hash *= 0xc2b2ae35;
hash ^= hash >> 16;
return hash;
}
uint MurmurHash(uint x, uint seed = 0)
{
uint h = seed;
h = MurmurAdd(h, x);
return MurmurFinalize(h);
}
uint MurmurHash(uint2 x, uint seed = 0)
{
uint h = seed;
h = MurmurAdd(h, x.x);
h = MurmurAdd(h, x.y);
return MurmurFinalize(h);
}
uint MurmurHash(uint3 x, uint seed = 0)
{
uint h = seed;
h = MurmurAdd(h, x.x);
h = MurmurAdd(h, x.y);
h = MurmurAdd(h, x.z);
return MurmurFinalize(h);
}
uint MurmurHash(uint4 x, uint seed = 0)
{
uint h = seed;
h = MurmurAdd(h, x.x);
h = MurmurAdd(h, x.y);
h = MurmurAdd(h, x.z);
h = MurmurAdd(h, x.w);
return MurmurFinalize(h);
}
uint XorShift32(uint rngState)
{
rngState ^= rngState << 13;
rngState ^= rngState >> 17;
rngState ^= rngState << 5;
return rngState;
}
// From PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation.
// and "Hash Functions for GPU Rendering" paper
uint Pcg(uint v)
{
uint state = v * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
uint2 Pcg2d(uint2 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y * 1664525u;
v.y += v.x * 1664525u;
v = v ^ (v >> 16u);
v.x += v.y * 1664525u;
v.y += v.x * 1664525u;
v = v ^ (v >> 16u);
return v;
}
uint3 Pcg3d(uint3 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
v ^= v >> 16u;
v.x += v.y * v.z;
v.y += v.z * v.x;
v.z += v.x * v.y;
return v;
}
uint4 Pcg4d(uint4 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;
v = v ^ (v >> 16u);
v.x += v.y * v.w;
v.y += v.z * v.x;
v.z += v.x * v.y;
v.w += v.y * v.z;
return v;
}
#endif // _SAMPLING_HASHES_HLSL_

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Hashes.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 9d187787267285249aa69304b4588bc4
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

268
Packages/com.unity.rendering.light-transport/Runtime/Sampling/QuasiRandom.hlsl

@ -0,0 +1,268 @@
#ifndef _SAMPLING_RANDOM_HLSL_
#define _SAMPLING_RANDOM_HLSL_
#include "Common.hlsl"
#include "Hashes.hlsl"
// Low discrepancy sequence generator with various implementations available
/* One of the following must be defined by the file that includes QuasiRandom.hlsl to select an implementation:
- QRNG_METHOD_SOBOL (Sobol sampler with Owen scrambling, from paper: Practical Hash-based Owen Scrambling by Burley)
infinite dims, 2097151 max samples, pixel tiling wraps at 65536
define QRNG_SOBOL_GENERATIVE_DIMS <multipleOf2> to control how many source dimensions are used, Default value 1024
- QRNG_METHOD_SOBOL_BLUE_NOISE (from paper: "A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space" by Heitz and Belcour)
256 max dims, 256 max samples (beyond 256, the sequence keeps going with another set of 256 samples belonging to another dim, and so on every 256 samples), pixel tiling wraps at 128
- QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE (from paper: "Screen-Space Blue-Noise Diffusion of Monte Carlo Sampling Error via Hierarchical Ordering of Pixels" by Ahmed and Wonka)
infinite dims and samples, pixel tiling depends on target sample count. The more samples, the smaller the tile (ex: for 256 samples, tiling size is 4096)
define QRNG_GLOBAL_SOBOL_ENHANCED_TILING to get tiling to always wrap at 65536
define QRNG_SOBOL_GENERATIVE_DIMS <multipleOf2> to control how many source dimensions are used, Default value 1024
- QRNG_METHOD_KRONECKER (Kronecker sequence from paper "Optimizing Kronecker Sequences for Multidimensional Sampling")
fast but lower quality than Sobol, infinite dims and samples, pixel tiling wraps at 65536
define QRNG_KRONECKER_ENHANCED_QUALITY to add small scale jitter
These last 2 aren't low discrepancy sequences but traditional Pseudorandom number generators
- QRNG_METHOD_RANDOM_XOR_SHIFT Xor shift PRNG
- QRNG_METHOD_RANDOM_PCG_4D (from paper: "Hash Functions for GPU Rendering" by Jarzynski & Olano)
*/
#if defined(QRNG_METHOD_SOBOL) || defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
#include "SobolSampling.hlsl"
#ifndef QRNG_SOBOL_GENERATIVE_DIMS
#define QRNG_SOBOL_GENERATIVE_DIMS SOBOL_MATRICES_COUNT
#endif
static const uint kMaxSobolDim = QRNG_SOBOL_GENERATIVE_DIMS;
#endif
uint PixelHash(uint2 pixelCoord, uint seed = 0)
{
return LowBiasHash32((pixelCoord.x & 0xFFFF) | (pixelCoord.y << 16), seed);
}
#if defined(QRNG_METHOD_SOBOL)
struct QuasiRandomGenerator
{
uint pixelSeed;
uint sampleIndex;
void Init(uint2 pixelCoord, uint startSampleIndex)
{
pixelSeed = PixelHash(pixelCoord);
sampleIndex = startSampleIndex;
}
float GetFloat(uint dimension)
{
uint scrambleSeed = LowBiasHash32(pixelSeed, dimension);
uint shuffleSeed = pixelSeed;
return GetOwenScrambledSobolSample(sampleIndex ^ shuffleSeed, dimension % kMaxSobolDim, scrambleSeed);
}
void NextSample()
{
sampleIndex++;
}
};
#elif defined(QRNG_METHOD_SOBOL_BLUE_NOISE)
#include "SobolBluenoiseSampling.hlsl"
struct QuasiRandomGenerator
{
uint2 pixelCoord;
uint sampleIndex;
void Init(uint2 pixelCoord_, uint startSampleIndex)
{
pixelCoord = pixelCoord_;
sampleIndex = startSampleIndex;
}
float GetFloat(uint dimension)
{
// If we go past the number of stored samples per dim, just shift all to the next pair of dimensions
dimension += (sampleIndex / 256) * 2;
return GetBNDSequenceSample(pixelCoord, sampleIndex, dimension);
}
void NextSample()
{
sampleIndex++;
}
};
#elif defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
struct QuasiRandomGenerator
{
uint pixelMortonCode;
uint log2SamplesPerPixel;
uint sampleIndex;
void Init(uint2 pixelCoord, uint startSampleIndex, uint perPixelSampleCount = 256)
{
pixelMortonCode = EncodeMorton2D(pixelCoord);
log2SamplesPerPixel = Log2IntUp(perPixelSampleCount);
sampleIndex = startSampleIndex;
}
float GetFloat(uint dimension)
{
return GetOwenScrambledZShuffledSobolSample(sampleIndex, dimension, kMaxSobolDim, pixelMortonCode, log2SamplesPerPixel);
}
void NextSample()
{
sampleIndex++;
}
};
#elif defined(QRNG_METHOD_KRONECKER)
struct QuasiRandomGenerator
{
uint cranleyPattersonSeed;
uint shuffledSampleIndex;
#ifdef QRNG_KRONECKER_ENHANCED_QUALITY
int sampleIndex;
#endif
void Init(uint2 pixelCoord, uint startSampleIndex)
{
uint hash = PixelHash(pixelCoord);
cranleyPattersonSeed = hash;
uint shuffledStartIndex = (startSampleIndex + hash) % (1 << 20);
shuffledSampleIndex = shuffledStartIndex;
#ifdef QRNG_KRONECKER_ENHANCED_QUALITY
sampleIndex = startSampleIndex+1;
#endif
}
float GetFloat(uint dimension)
{
const uint alphas[]= { // values are stored multiplied by (1 << 32)
// R2 from http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/
3242174889, 2447445414,
// K21_2 from Optimizing Kronecker Sequences for Multidimensional Sampling
3316612456, 1538627358,
};
// compute random offset to apply to the sequence (using another Kronecker sequence)
uint cranleyPattersonRot = cranleyPattersonSeed + 3646589397 * (dimension / 4);
#ifdef QRNG_KRONECKER_ENHANCED_QUALITY // add small scale jitter as explained in paper
const float alphaJitter[] = { 2681146017, 685201898 };
uint jitter = alphaJitter[dimension % 2] * shuffledSampleIndex;
float amplitude = 0.05 * 0.78 / sqrt(2) * rsqrt(float(sampleIndex));
cranleyPattersonRot += jitter * uint(amplitude);
#endif
// Kronecker sequence evaluation
return UintToFloat01(cranleyPattersonRot + alphas[dimension % 4] * shuffledSampleIndex);
}
void NextSample()
{
// shuffledSampleIndex modulo 1048576 to avoid numerical precision issues when evaluating the Kronecker sequence
shuffledSampleIndex = (shuffledSampleIndex + 1) % (1 << 20);
#ifdef QRNG_KRONECKER_ENHANCED_QUALITY
sampleIndex++;
#endif
}
};
#elif defined(QRNG_METHOD_RANDOM_XOR_SHIFT)
struct QuasiRandomGenerator
{
uint state;
void Init(uint2 pixelCoord, uint startSampleIndex)
{
state = PixelHash(pixelCoord, startSampleIndex);
}
float GetFloat(uint dimension)
{
state = XorShift32(state);
return UintToFloat01(state);
}
void NextSample()
{
}
};
#elif defined(QRNG_METHOD_RANDOM_PCG_4D)
struct QuasiRandomGenerator
{
uint4 state;
void Init(uint2 pixelCoord, uint startSampleIndex)
{
// Seed for PCG uses a sequential sample number in 4th channel, which increments on every RNG call and starts from 0
state = uint4(pixelCoord, startSampleIndex, 0);
}
float GetFloat(int dimension)
{
state.w++;
return UintToFloat01(Pcg4d(state).x);
}
void NextSample()
{
state.z++;
}
};
#endif
// global dimension offset (could be used to alter the noise pattern)
#ifndef QRNG_OFFSET
#define QRNG_OFFSET 0
#endif
#ifndef QRNG_SAMPLES_PER_BOUNCE
#define QRNG_SAMPLES_PER_BOUNCE 64
#endif
struct PathTracingSampler
{
QuasiRandomGenerator generator;
int bounceIndex;
void Init(uint2 pixelCoord, uint startPathIndex, uint perPixelPathCount = 256)
{
#if defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
generator.Init(pixelCoord, startPathIndex, perPixelPathCount);
#else
generator.Init(pixelCoord, startPathIndex);
#endif
bounceIndex = 0;
}
float GetFloatSample(int dimension)
{
uint actualDimension = QRNG_OFFSET + QRNG_SAMPLES_PER_BOUNCE * bounceIndex + dimension;
return generator.GetFloat(actualDimension);
}
void NextBounce()
{
bounceIndex++;
}
void NextPath()
{
generator.NextSample();
bounceIndex = 0;
}
};
#endif // _SAMPLING_RANDOM_HLSL_

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/QuasiRandom.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 42d054f9eda3d4c4abfd3b0e7903cc47
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

70
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.cs

@ -0,0 +1,70 @@
using System;
using System.Runtime.InteropServices;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.Rendering.Sampling
{
internal sealed class SamplingResources : IDisposable
{
internal enum ResourceType
{
BlueNoiseTextures = 1,
SobolMatrices = 2,
All = BlueNoiseTextures | SobolMatrices
};
private Texture2D m_SobolScramblingTile;
private Texture2D m_SobolRankingTile;
private Texture2D m_SobolOwenScrambled256Samples;
private GraphicsBuffer m_SobolBuffer;
static public readonly uint[] sobolMatrices = SobolData.SobolMatrices;
#if UNITY_EDITOR
public void Load(uint resourceBitmask = (uint)ResourceType.BlueNoiseTextures)
{
if ((resourceBitmask & (uint)ResourceType.BlueNoiseTextures) != 0)
{
const string path = "Packages/com.unity.rendering.light-transport/Runtime/";
m_SobolScramblingTile = AssetDatabase.LoadAssetAtPath<Texture2D>(path + "Sampling/Textures/SobolBlueNoise/ScramblingTile256SPP.png");
m_SobolRankingTile = AssetDatabase.LoadAssetAtPath<Texture2D>(path + "Sampling/Textures/SobolBlueNoise/RankingTile256SPP.png");
m_SobolOwenScrambled256Samples = AssetDatabase.LoadAssetAtPath<Texture2D>(path + "Sampling/Textures/SobolBlueNoise/SobolOwenScrambled256.png");
}
if ((resourceBitmask & (uint)ResourceType.SobolMatrices) != 0)
{
int sobolBufferSize = (int)(SobolData.SobolDims * SobolData.SobolSize);
m_SobolBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, sobolBufferSize, Marshal.SizeOf<uint>());
m_SobolBuffer.SetData(SobolData.SobolMatrices);
}
}
#endif
public static void BindSobolBlueNoiseTextures(CommandBuffer cmd, SamplingResources resources)
{
if (resources.m_SobolScramblingTile != null)
{
cmd.SetGlobalTexture(Shader.PropertyToID("_SobolScramblingTile"), resources.m_SobolScramblingTile);
cmd.SetGlobalTexture(Shader.PropertyToID("_SobolRankingTile"), resources.m_SobolRankingTile);
cmd.SetGlobalTexture(Shader.PropertyToID("_SobolOwenScrambledSequence"), resources.m_SobolOwenScrambled256Samples);
}
if (resources.m_SobolBuffer != null)
cmd.SetGlobalBuffer("_SobolMatricesBuffer", resources.m_SobolBuffer);
}
public void Dispose()
{
m_SobolBuffer?.Dispose();
}
}
}

2
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 41bb54c77a08c08438f97423537a16e6

14
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.hlsl

@ -0,0 +1,14 @@
#ifndef _SAMPLING_SAMPLINGRESOURCES_HLSL_
#define _SAMPLING_SAMPLINGRESOURCES_HLSL_
#ifdef QRNG_METHOD_SOBOL_BLUE_NOISE
Texture2D<float> _SobolScramblingTile;
Texture2D<float> _SobolRankingTile;
Texture2D<float2> _SobolOwenScrambledSequence;
#endif
#if defined(QRNG_METHOD_SOBOL) || defined(QRNG_METHOD_GLOBAL_SOBOL_BLUE_NOISE)
StructuredBuffer<uint> _SobolMatricesBuffer;
#endif
#endif

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SamplingResources.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 87490cc9fe2f28f4890dcdb720bf0c94
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

31
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolBluenoiseSampling.hlsl

@ -0,0 +1,31 @@
#ifndef _SAMPLING_SOBOLBLUENOISESAMPLING_HLSL_
#define _SAMPLING_SOBOLBLUENOISESAMPLING_HLSL_
#include "SamplingResources.hlsl"
// This is an implementation of the method from the paper
// "A Low-Discrepancy Sampler that Distributes Monte Carlo Errors as a Blue Noise in Screen Space" by Heitz et al.
float GetBNDSequenceSample(uint2 pixelCoord, uint sampleIndex, uint sampleDimension)
{
// wrap arguments
pixelCoord = pixelCoord & 127;
sampleIndex = sampleIndex & 255;
sampleDimension = sampleDimension & 255;
// xor index based on optimized ranking
uint rankingIndex = (pixelCoord.x + pixelCoord.y * 128) * 8 + (sampleDimension & 7);
uint rankedSampleIndex = sampleIndex ^ clamp((uint)(_SobolRankingTile[uint2(rankingIndex & 127, rankingIndex / 128)] * 256.0), 0, 255);
// fetch value in sequence
uint value = clamp((uint)(_SobolOwenScrambledSequence[uint2(sampleDimension, rankedSampleIndex.x)] * 256.0), 0, 255);
// If the dimension is optimized, xor sequence value based on optimized scrambling
uint scramblingIndex = (pixelCoord.x + pixelCoord.y * 128) * 8 + (sampleDimension & 7);
float scramblingValue = min(_SobolScramblingTile[uint2(scramblingIndex & 127, scramblingIndex / 128)], 0.999);
value = value ^ uint(scramblingValue * 256.0);
// Convert to float (to avoid the same 1/256th quantization everywhere, we jitter by the pixel scramblingValue)
return (max(0.001, scramblingValue) + value) / 256.0;
}
#endif // _SAMPLING_SOBOLBLUENOISESAMPLING_HLSL_

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolBluenoiseSampling.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b7e54860bc0a3514f9dc1524fde27b07
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

53288
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolData.cs
File diff suppressed because it is too large
View File

2
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolData.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4ca8401bf97c614468dbc0101d45d276

169
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolSampling.hlsl

@ -0,0 +1,169 @@
#ifndef _SAMPLING_SOBOLSAMPLING_HLSL_
#define _SAMPLING_SOBOLSAMPLING_HLSL_
#define SOBOL_MATRIX_SIZE 52
#define SOBOL_MATRICES_COUNT 1024
#include "SamplingResources.hlsl"
#include "Hashes.hlsl"
#include "Common.hlsl"
// HLSLcc cannot correctly translate `reversebits(x)` for large unsigned integers.
// Therefore, when using HLSLcc, we use our own implementation. https://jira.unity3d.com/browse/GFXFEAT-629
#ifdef UNITY_COMPILER_HLSLCC
uint ReverseBitsSafe(uint x)
{
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8);
x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16);
return x;
}
#else
#define ReverseBitsSafe reversebits
#endif
// See https://psychopath.io/post/2021_01_30_building_a_better_lk_hash
uint LaineKarrasPermutation(uint x, uint seed)
{
x ^= x * 0x3d20adea;
x += seed;
x *= (seed >> 16) | 1;
x ^= x * 0x05526c56;
x ^= x * 0x53a22864;
return x;
}
uint NestedUniformOwenScramble(uint x, uint seed)
{
x = ReverseBitsSafe(x);
x = LaineKarrasPermutation(x, seed);
x = ReverseBitsSafe(x);
return x;
}
//See https://psychopath.io/post/2022_08_14_a_fast_hash_for_base_4_owen_scrambling
uint LaineKarrasStylePermutationBase4(uint x, uint seed)
{
x ^= x * 0x3d20adeau;
x ^= (x >> 1) & (x << 1) & 0x55555555u;
x += seed;
x *= (seed >> 16) | 1;
x ^= (x >> 1) & (x << 1) & 0x55555555u;
x ^= x * 0x05526c56u;
x ^= x * 0x53a22864u;
return x;
}
uint NestedUniformScrambleBase4(uint x, uint seed)
{
x = ReverseBitsSafe(x);
x = LaineKarrasStylePermutationBase4(x, seed);
x = ReverseBitsSafe(x);
return x;
}
// "Insert" a 0 bit after each of the 16 low bits of x.
// Ref: https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
uint Part1By1(uint x)
{
x &= 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
return x;
}
uint EncodeMorton2D(uint2 coord)
{
return (Part1By1(coord.y) << 1) + Part1By1(coord.x);
}
uint SobolSampleUint(uint index, int dimension, uint indexMaxBitCount)
{
uint result = 0;
#ifdef UNIFIED_RT_BACKEND_HARDWARE
// Raytracing tracing shaders compile only with unrolled loop, but unrolling fails in the compute path.
// TODO: test again when Unity updates the shader compiler
[unroll]
#endif
for (uint i = dimension * SOBOL_MATRIX_SIZE; i < dimension * SOBOL_MATRIX_SIZE + indexMaxBitCount; index >>= 1, ++i)
{
result ^= _SobolMatricesBuffer[i] * (index & 1);
}
return result;
}
uint SobolSampleUint(uint2 index64, uint dimension)
{
uint result = 0;
uint i = dimension * SOBOL_MATRIX_SIZE;
for (; i < dimension * SOBOL_MATRIX_SIZE + 32; index64.x >>= 1, ++i)
{
result ^= _SobolMatricesBuffer[i] * (index64.x & 1);
}
for (; i < dimension * SOBOL_MATRIX_SIZE + 32+20; index64.y >>= 1, ++i)
{
result ^= _SobolMatricesBuffer[i] * (index64.y & 1);
}
return result;
}
uint GetMortonShuffledSampleIndex(uint index, uint pixelMortonIndex, uint mortonShuffleSeed, uint log2SamplesPerPixel)
{
uint mortonIndexShuffled = NestedUniformScrambleBase4(pixelMortonIndex, mortonShuffleSeed);
uint sampleIndexForPixel = (mortonIndexShuffled << log2SamplesPerPixel) | index;
return sampleIndexForPixel;
}
uint2 GetMortonShuffledSampleIndex64(uint index, uint pixelMortonIndex, uint mortonShuffleSeed, uint log2SamplesPerPixel)
{
uint mortonIndexShuffled = NestedUniformScrambleBase4(pixelMortonIndex, mortonShuffleSeed);
uint2 sampleIndex64;
sampleIndex64.x = (mortonIndexShuffled << log2SamplesPerPixel) | index;
sampleIndex64.y = mortonIndexShuffled >> (32 - log2SamplesPerPixel);
return sampleIndex64;
}
float GetSobolSample(uint index, int dimension)
{
uint result = SobolSampleUint(index, dimension, 20);
return UintToFloat01(result);
}
float GetOwenScrambledSobolSample(uint sampleIndex, uint dim, uint valueScrambleSeed)
{
uint sobolUInt = SobolSampleUint(sampleIndex, dim, 20);
uint result = NestedUniformOwenScramble(sobolUInt, valueScrambleSeed);
return UintToFloat01(result);
}
float GetOwenScrambledZShuffledSobolSample(uint sampleIndex, uint dim, uint maxDim, uint pixelMortonCode, uint log2SamplesPerPixel)
{
const uint kSeedValueScramble = 0xab773au;
uint mortonShuffleSeed = LowBiasHash32(dim, 0);
uint valueScrambleSeed = LowBiasHash32(dim, kSeedValueScramble);
#ifdef QRNG_GLOBAL_SOBOL_ENHANCED_TILING
uint2 shuffledSampleIndex = GetMortonShuffledSampleIndex64(sampleIndex, pixelMortonCode, mortonShuffleSeed, log2SamplesPerPixel);
uint sobolUInt = SobolSampleUint(shuffledSampleIndex, dim % maxDim);
#else
uint shuffledSampleIndex = GetMortonShuffledSampleIndex(sampleIndex, pixelMortonCode, mortonShuffleSeed, log2SamplesPerPixel);
uint sobolUInt = SobolSampleUint(shuffledSampleIndex, dim % maxDim, 32);
#endif
uint result = NestedUniformOwenScramble(sobolUInt, valueScrambleSeed);
return UintToFloat01(result);
}
#endif // UNITY_SOBOL_SAMPLING_INCLUDED

7
Packages/com.unity.rendering.light-transport/Runtime/Sampling/SobolSampling.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d0586908835c55944aa8bf3426243186
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 317c6ff5b8d44894b8530492822044be
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5055d486e2e33654a86c88d9238d3de5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/RankingTile256SPP.png

After

Width: 128  |  Height: 1024  |  Size: 84 KiB

127
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/RankingTile256SPP.png.meta

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 5307d6e84442b0849b7ee235964e578d
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 10
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 0
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: PS4
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/ScramblingTile256SPP.png

After

Width: 128  |  Height: 1024  |  Size: 129 KiB

127
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/ScramblingTile256SPP.png.meta

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 7cb0041122895034eb8f6692eed6fe14
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 10
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: -1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 0
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: PS4
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

BIN
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/SobolOwenScrambled256.png

After

Width: 256  |  Height: 256  |  Size: 64 KiB

242
Packages/com.unity.rendering.light-transport/Runtime/Sampling/Textures/SobolBlueNoise/SobolOwenScrambled256.png.meta

@ -0,0 +1,242 @@
fileFormatVersion: 2
guid: b068695e42b4d4c3ba751c10a7189033
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 0
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: PS4
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: PS5
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: GameCoreXboxOne
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: GameCoreScarlett
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Windows Store Apps
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Stadia
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Lumin
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: EmbeddedLinux
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 310f0971833fd8541ab0d52464fad0ac
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

133
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Bindings.hlsl

@ -0,0 +1,133 @@
#ifndef _UNIFIEDRAYTRACING_BINDINGS_HLSL_
#define _UNIFIEDRAYTRACING_BINDINGS_HLSL_
#if defined(UNIFIED_RT_BACKEND_COMPUTE)
#ifndef UNIFIED_RT_GROUP_SIZE_X
#define UNIFIED_RT_GROUP_SIZE_X 16
#endif
#ifndef UNIFIED_RT_GROUP_SIZE_Y
#define UNIFIED_RT_GROUP_SIZE_Y 8
#endif
#ifndef UNIFIED_RT_GROUP_SIZE_Z
#define UNIFIED_RT_GROUP_SIZE_Z 1
#endif
#define GROUP_SIZE (UNIFIED_RT_GROUP_SIZE_X*UNIFIED_RT_GROUP_SIZE_Y)
#include "Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/kernels/trace_ray.hlsl"
#endif
namespace UnifiedRT {
struct Ray
{
float3 origin;
float tMin;
float3 direction;
float tMax;
};
struct Hit
{
uint instanceID;
uint primitiveIndex;
float2 uvBarycentrics;
float hitDistance;
bool isFrontFace;
bool IsValid()
{
return instanceID != -1;
}
static Hit Invalid()
{
Hit hit = (Hit)0;
hit.instanceID = -1;
return hit;
}
};
struct InstanceData
{
float4x4 localToWorld;
float4x4 previousLocalToWorld;
float4x4 localToWorldNormals;
uint renderingLayerMask;
uint instanceMask;
uint userMaterialID;
uint geometryIndex;
};
struct DispatchInfo
{
uint3 dispatchThreadID;
uint localThreadIndex;
uint3 dispatchDimensionsInThreads;
uint globalThreadIndex;
};
struct RayTracingAccelStruct
{
#if defined(UNIFIED_RT_BACKEND_HARDWARE)
RaytracingAccelerationStructure accelStruct;
#elif defined(UNIFIED_RT_BACKEND_COMPUTE)
StructuredBuffer<BvhNode> bvh;
StructuredBuffer<BvhNode> bottom_bvhs;
StructuredBuffer<uint4> bottom_bvh_leaves;
StructuredBuffer<InstanceInfo> instance_infos;
StructuredBuffer<uint> vertexBuffer;
int vertexStride;
#else
#pragma message("Error, you must define either UNIFIED_RT_BACKEND_HARDWARE or UNIFIED_RT_BACKEND_COMPUTE")
#endif
};
#if defined(UNIFIED_RT_BACKEND_HARDWARE)
RayTracingAccelStruct GetAccelStruct(RaytracingAccelerationStructure accelStruct)
{
RayTracingAccelStruct res;
res.accelStruct = accelStruct;
return res;
}
#define UNIFIED_RT_DECLARE_ACCEL_STRUCT(name) RaytracingAccelerationStructure name##accelStruct
#define UNIFIED_RT_GET_ACCEL_STRUCT(name) UnifiedRT::GetAccelStruct(name##accelStruct)
#elif defined(UNIFIED_RT_BACKEND_COMPUTE)
RayTracingAccelStruct GetAccelStruct(
StructuredBuffer<BvhNode> bvh,
StructuredBuffer<BvhNode> bottomBvhs,
StructuredBuffer<uint4> bottomBvhLeaves,
StructuredBuffer<InstanceInfo> instanceInfos,
StructuredBuffer<uint> vertexBuffer,
int vertexStride)
{
RayTracingAccelStruct res;
res.bvh = bvh;
res.bottom_bvhs = bottomBvhs;
res.bottom_bvh_leaves = bottomBvhLeaves;
res.instance_infos = instanceInfos;
res.vertexBuffer = vertexBuffer;
res.vertexStride = vertexStride;
return res;
}
#define UNIFIED_RT_DECLARE_ACCEL_STRUCT(name) StructuredBuffer<BvhNode> name##bvh; StructuredBuffer<BvhNode> name##bottomBvhs; StructuredBuffer<uint4> name##bottomBvhLeaves; StructuredBuffer<InstanceInfo> name##instanceInfos; StructuredBuffer<uint> name##vertexBuffer; int name##vertexStride
#define UNIFIED_RT_GET_ACCEL_STRUCT(name) UnifiedRT::GetAccelStruct(name##bvh, name##bottomBvhs, name##bottomBvhLeaves, name##instanceInfos, name##vertexBuffer, name##vertexStride)
#endif
} // namespace UnifiedRT
#if defined(UNIFIED_RT_BACKEND_COMPUTE)
RWStructuredBuffer<uint> g_stack;
#endif
#endif // UNIFIEDRAYTRACING_BINDINGS_HLSL

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Bindings.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 612d23a31bb951c48ae167c31ee0f878
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

32
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.hlsl

@ -0,0 +1,32 @@
#ifndef _UNIFIEDRAYTRACING_COMMON_HLSL_
#define _UNIFIEDRAYTRACING_COMMON_HLSL_
#include "Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Bindings.hlsl"
#include "Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/FetchGeometry.hlsl"
#define K_T_MAX 400000
#ifndef FLT_EPSILON
#define FLT_EPSILON 1.192092896e-07F
#endif
#ifndef FLT_MAX
#define FLT_MAX 3.402823e+38
#endif
float Max3(float3 val)
{
return max(max(val.x, val.y), val.z);
}
// Adapted from RayTracing Gems, A Fast and Robust Method for Avoiding Self-Intersection
// - Dropped the exact +N ulp computation, instead use, N * epsilon
// - Use max of distance components instead of per component offset
// - Use less conservative factors for error estimation
float3 OffsetRayOrigin(float3 p, float3 n, float customOffset = 0.0f)
{
float distanceToOrigin = Max3(abs(p));
float offset = (distanceToOrigin < 1 / 32.0f) ? FLT_EPSILON * 64.0f : FLT_EPSILON * 64.0f * distanceToOrigin;
return p + (offset + customOffset) * n;
}
#endif

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6dd8f4aba68004c88877200e0d8e7ec4
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dab83bd6a48c6914e8aa6103773d5bb6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

295
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs

@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
using Unity.Mathematics;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal sealed class AccelStructAdapter : IDisposable
{
private IRayTracingAccelStruct _accelStruct;
AccelStructInstances _instances;
internal AccelStructInstances Instances { get => _instances; }
struct InstanceIDs
{
public int InstanceID;
public int AccelStructID;
}
private readonly Dictionary<int, InstanceIDs[]> _objectHandleToInstances = new();
public AccelStructAdapter(IRayTracingAccelStruct accelStruct, GeometryPool geometryPool)
{
_accelStruct = accelStruct;
_instances = new AccelStructInstances(geometryPool);
}
public AccelStructAdapter(IRayTracingAccelStruct accelStruct, RayTracingResources resources)
: this(accelStruct, new GeometryPool(GeometryPoolDesc.NewDefault(), resources.geometryPoolKernels, resources.copyBuffer))
{ }
public IRayTracingAccelStruct GetAccelerationStructure()
{
return _accelStruct;
}
public GeometryPool GeometryPool => _instances.geometryPool;
public void Bind(CommandBuffer cmd, string propertyName, IRayTracingShader shader)
{
shader.SetAccelerationStructure(cmd, propertyName, _accelStruct);
_instances.Bind(cmd, shader);
}
public void Dispose()
{
_instances?.Dispose();
_instances = null;
_accelStruct?.Dispose();
_accelStruct = null;
_objectHandleToInstances.Clear();
}
public void AddInstance(int objectHandle, Component meshRendererOrTerrain, Span<uint> perSubMeshMask, Span<uint> perSubMeshMaterialIDs, uint renderingLayerMask)
{
if (meshRendererOrTerrain is Terrain terrain)
{
Debug.Assert(terrain.enabled, "Terrains are expected to be enabled.");
TerrainDesc terrainDesc;
terrainDesc.terrain = terrain;
terrainDesc.localToWorldMatrix = terrain.transform.localToWorldMatrix;
terrainDesc.mask = perSubMeshMask[0];
terrainDesc.renderingLayerMask = renderingLayerMask;
terrainDesc.materialID = perSubMeshMaterialIDs[0];
terrainDesc.enableTriangleCulling = true;
terrainDesc.frontTriangleCounterClockwise = false;
AddInstance(objectHandle, terrainDesc);
}
else
{
var meshRenderer = (MeshRenderer)meshRendererOrTerrain;
Debug.Assert(meshRenderer.enabled, "Mesh renderers are expected to be enabled.");
Debug.Assert(!meshRenderer.isPartOfStaticBatch, "Mesh renderers are expected to not be part of static batch.");
var mesh = meshRenderer.GetComponent<MeshFilter>().sharedMesh;
AddInstance(objectHandle, mesh, meshRenderer.transform.localToWorldMatrix, perSubMeshMask, perSubMeshMaterialIDs, renderingLayerMask);
}
}
public void AddInstance(int objectHandle, Mesh mesh, Matrix4x4 localToWorldMatrix, Span<uint> perSubMeshMask, Span<uint> perSubMeshMaterialIDs, uint renderingLayerMask)
{
int subMeshCount = mesh.subMeshCount;
var instances = new InstanceIDs[subMeshCount];
for (int i = 0; i < subMeshCount; ++i)
{
var instanceDesc = new MeshInstanceDesc(mesh, i)
{
localToWorldMatrix = localToWorldMatrix,
mask = perSubMeshMask[i],
};
instances[i].InstanceID = _instances.AddInstance(instanceDesc, perSubMeshMaterialIDs[i], renderingLayerMask);
instanceDesc.instanceID = (uint)instances[i].InstanceID;
instances[i].AccelStructID = _accelStruct.AddInstance(instanceDesc);
}
_objectHandleToInstances.Add(objectHandle, instances);
}
private void AddInstance(int objectHandle, TerrainDesc terrainDesc)
{
List<InstanceIDs> instanceHandles = new List<InstanceIDs>();
AddHeightmap(terrainDesc, ref instanceHandles);
AddTrees(terrainDesc, ref instanceHandles);
_objectHandleToInstances.Add(objectHandle, instanceHandles.ToArray());
}
void AddHeightmap(TerrainDesc terrainDesc, ref List<InstanceIDs> instanceHandles)
{
var terrainMesh = TerrainToMesh.Convert(terrainDesc.terrain);
var instanceDesc = new MeshInstanceDesc(terrainMesh);
instanceDesc.localToWorldMatrix = terrainDesc.localToWorldMatrix;
instanceDesc.mask = terrainDesc.mask;
instanceDesc.enableTriangleCulling = terrainDesc.enableTriangleCulling;
instanceDesc.frontTriangleCounterClockwise = terrainDesc.frontTriangleCounterClockwise;
instanceHandles.Add(AddInstance(instanceDesc, terrainDesc.materialID, terrainDesc.renderingLayerMask));
}
void AddTrees(TerrainDesc terrainDesc, ref List<InstanceIDs> instanceHandles)
{
TerrainData terrainData = terrainDesc.terrain.terrainData;
float4x4 terrainLocalToWorld = terrainDesc.localToWorldMatrix;
float3 positionScale = new float3((float)terrainData.heightmapResolution, 1.0f, (float)terrainData.heightmapResolution) * terrainData.heightmapScale;
float3 positionOffset = new float3(terrainLocalToWorld[3].x, terrainLocalToWorld[3].y, terrainLocalToWorld[3].z);
foreach (var treeInstance in terrainData.treeInstances)
{
var localToWorld = Matrix4x4.TRS(
positionOffset + new float3(treeInstance.position) * positionScale,
Quaternion.AngleAxis(treeInstance.rotation, Vector3.up),
new Vector3(treeInstance.widthScale, treeInstance.heightScale, treeInstance.widthScale));
var prefab = terrainData.treePrototypes[treeInstance.prototypeIndex].prefab;
GameObject go = prefab.gameObject;
if (prefab.TryGetComponent<LODGroup>(out var lodGroup))
{
var groups = lodGroup.GetLODs();
if (groups.Length != 0 && groups[0].renderers.Length != 0)
go = (groups[0].renderers[0] as MeshRenderer).gameObject;
}
if (!go.TryGetComponent<MeshFilter>(out var filter))
continue;
var mesh = filter.sharedMesh;
for (int i = 0; i < mesh.subMeshCount; ++i)
{
var instanceDesc = new MeshInstanceDesc(mesh, i);
instanceDesc.localToWorldMatrix = localToWorld;
instanceDesc.mask = terrainDesc.mask;
instanceDesc.enableTriangleCulling = terrainDesc.enableTriangleCulling;
instanceDesc.frontTriangleCounterClockwise = terrainDesc.frontTriangleCounterClockwise;
instanceHandles.Add(AddInstance(instanceDesc, terrainDesc.materialID, 1u << prefab.gameObject.layer));
}
}
}
InstanceIDs AddInstance(MeshInstanceDesc instanceDesc, uint materialID, uint renderingLayerMask)
{
InstanceIDs res = new InstanceIDs();
res.InstanceID = _instances.AddInstance(instanceDesc, materialID, renderingLayerMask);
instanceDesc.instanceID = (uint)res.InstanceID;
res.AccelStructID = _accelStruct.AddInstance(instanceDesc);
return res;
}
public void RemoveInstance(int objectHandle)
{
bool success = _objectHandleToInstances.TryGetValue(objectHandle, out var instances);
Assert.IsTrue(success);
foreach (var instance in instances)
{
_instances.RemoveInstance(instance.InstanceID);
_accelStruct.RemoveInstance(instance.AccelStructID);
}
_objectHandleToInstances.Remove(objectHandle);
}
public void UpdateInstanceTransform(int objectHandle, Matrix4x4 localToWorldMatrix)
{
bool success = _objectHandleToInstances.TryGetValue(objectHandle, out var instances);
Assert.IsTrue(success);
foreach(var instance in instances)
{
_instances.UpdateInstanceTransform(instance.InstanceID, localToWorldMatrix);
_accelStruct.UpdateInstanceTransform(instance.AccelStructID, localToWorldMatrix);
}
}
public void UpdateInstanceMaterialIDs(int objectHandle, Span<uint> perSubMeshMaterialIDs)
{
bool success = _objectHandleToInstances.TryGetValue(objectHandle, out var instances);
Assert.IsTrue(success);
Assert.IsTrue(perSubMeshMaterialIDs.Length >= instances.Length);
int i = 0;
foreach (var instance in instances)
{
_instances.UpdateInstanceMaterialID(instance.InstanceID, perSubMeshMaterialIDs[i++]);
}
}
public void UpdateInstanceMask(int objectHandle, Span<uint> perSubMeshMask)
{
bool success = _objectHandleToInstances.TryGetValue(objectHandle, out var instances);
Assert.IsTrue(success);
Assert.IsTrue(perSubMeshMask.Length >= instances.Length);
int i = 0;
foreach (var instance in instances)
{
_instances.UpdateInstanceMask(instance.InstanceID, perSubMeshMask[i]);
_accelStruct.UpdateInstanceMask(instance.AccelStructID, perSubMeshMask[i]);
i++;
}
}
public void UpdateInstanceMask(int objectHandle, uint mask)
{
bool success = _objectHandleToInstances.TryGetValue(objectHandle, out var instances);
Assert.IsTrue(success);
var perSubMeshMask = new uint[instances.Length];
Array.Fill(perSubMeshMask, mask);
int i = 0;
foreach (var instance in instances)
{
_instances.UpdateInstanceMask(instance.InstanceID, perSubMeshMask[i]);
_accelStruct.UpdateInstanceMask(instance.AccelStructID, perSubMeshMask[i]);
i++;
}
}
public void Build(CommandBuffer cmd, ref GraphicsBuffer scratchBuffer)
{
RayTracingHelper.ResizeScratchBufferForBuild(_accelStruct, ref scratchBuffer);
_accelStruct.Build(cmd, scratchBuffer);
}
public void NextFrame()
{
_instances.NextFrame();
}
public bool GetInstanceIDs(int rendererID, out int[] instanceIDs)
{
if (!_objectHandleToInstances.TryGetValue(rendererID, out InstanceIDs[] instIDs))
{
// This should never happen as long as the renderer was already added to the acceleration structure
instanceIDs = null;
return false;
}
instanceIDs = Array.ConvertAll(instIDs, item => item.InstanceID);
return true;
}
}
internal struct TerrainDesc
{
public Terrain terrain;
public Matrix4x4 localToWorldMatrix;
public uint mask;
public uint renderingLayerMask;
public uint materialID;
public bool enableTriangleCulling;
public bool frontTriangleCounterClockwise;
public TerrainDesc(Terrain terrain)
{
this.terrain = terrain;
localToWorldMatrix = Matrix4x4.identity;
mask = 0xFFFFFFFF;
renderingLayerMask = 0xFFFFFFFF;
materialID = 0;
enableTriangleCulling = true;
frontTriangleCounterClockwise = false;
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructAdapter.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 295102be7cbf62745a566ad91b3e1989
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

232
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructInstances.cs

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal sealed class AccelStructInstances : IDisposable
{
internal AccelStructInstances(GeometryPool geometryPool)
{
m_GeometryPool = geometryPool;
}
public void Dispose()
{
foreach (InstanceEntry instanceEntry in m_Instances.Values)
{
GeometryPoolHandle geomHandle = instanceEntry.geometryPoolHandle;
m_GeometryPool.Unregister(geomHandle);
}
m_GeometryPool.SendGpuCommands();
m_InstanceBuffer?.Dispose();
m_GeometryPool.Dispose();
}
public PersistentGpuArray<RTInstance> instanceBuffer { get => m_InstanceBuffer; }
public IReadOnlyCollection<InstanceEntry> instances { get => m_Instances.Values; }
public GeometryPool geometryPool { get => m_GeometryPool; }
public int AddInstance(MeshInstanceDesc meshInstance, uint materialID, uint renderingLayerMask)
{
var slot = m_InstanceBuffer.Add(1)[0];
AddInstance(slot, meshInstance, materialID, renderingLayerMask);
return slot.block.offset;
}
public int AddInstances(Span<MeshInstanceDesc> meshInstances, Span<uint> materialIDs, Span<uint> renderingLayerMask)
{
Assert.IsTrue(meshInstances.Length == materialIDs.Length);
var slots = m_InstanceBuffer.Add(meshInstances.Length);
for (int i = 0; i < meshInstances.Length; ++i)
AddInstance(slots[i], meshInstances[i], materialIDs[i], renderingLayerMask[i]);
return slots[0].block.offset;
}
void AddInstance(BlockAllocator.Allocation slotAllocation, in MeshInstanceDesc meshInstance, uint materialID, uint renderingLayerMask)
{
Debug.Assert(meshInstance.mesh != null, "targetRenderer.mesh is null");
GeometryPoolHandle geometryHandle;
if (!m_GeometryPool.Register(meshInstance.mesh, out geometryHandle))
throw new System.InvalidOperationException("Failed to allocate geometry data for instance");
m_GeometryPool.SendGpuCommands();
m_InstanceBuffer.Set(slotAllocation,
new RTInstance
{
localToWorld = meshInstance.localToWorldMatrix,
localToWorldNormals = NormalMatrix(meshInstance.localToWorldMatrix),
previousLocalToWorld = meshInstance.localToWorldMatrix,
userMaterialID = materialID,
instanceMask = meshInstance.mask,
renderingLayerMask = renderingLayerMask,
geometryIndex = (uint)(m_GeometryPool.GetEntryGeomAllocation(geometryHandle).meshChunkTableAlloc.block.offset + meshInstance.subMeshIndex)
});
var allocInfo = m_GeometryPool.GetEntryGeomAllocation(geometryHandle).meshChunks[meshInstance.subMeshIndex];
var instanceEntry = new InstanceEntry
{
geometryPoolHandle = geometryHandle,
indexInInstanceBuffer = slotAllocation,
instanceMask = meshInstance.mask,
vertexOffset = (uint)(allocInfo.vertexAlloc.block.offset) * ((uint)GeometryPool.GetVertexByteSize() / 4),
indexOffset = (uint)allocInfo.indexAlloc.block.offset,
};
m_Instances.Add(slotAllocation.block.offset, instanceEntry);
}
public GeometryPool.MeshChunk GetEntryGeomAllocation(GeometryPoolHandle handle, int submeshIndex)
{
return m_GeometryPool.GetEntryGeomAllocation(handle).meshChunks[submeshIndex];
}
public GraphicsBuffer indexBuffer { get { return m_GeometryPool.globalIndexBuffer; } }
public GraphicsBuffer vertexBuffer { get { return m_GeometryPool.globalVertexBuffer; } }
public void RemoveInstance(int instanceHandle)
{
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry removedEntry);
Assert.IsTrue(success);
m_Instances.Remove(instanceHandle);
m_InstanceBuffer.Remove(removedEntry.indexInInstanceBuffer);
var geomHandle = removedEntry.geometryPoolHandle;
m_GeometryPool.Unregister(geomHandle);
m_GeometryPool.SendGpuCommands();
}
public void ClearInstances()
{
foreach (InstanceEntry instanceEntry in m_Instances.Values)
{
GeometryPoolHandle geomHandle = instanceEntry.geometryPoolHandle;
m_GeometryPool.Unregister(geomHandle);
}
m_GeometryPool.SendGpuCommands();
m_Instances.Clear();
m_InstanceBuffer.Clear();
}
public void UpdateInstanceTransform(int instanceHandle, Matrix4x4 localToWorldMatrix)
{
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry instanceEntry);
Assert.IsTrue(success);
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
instanceInfo.localToWorld = localToWorldMatrix;
instanceInfo.localToWorldNormals = NormalMatrix(localToWorldMatrix);
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
m_TransformTouchedLastTimestamp = m_FrameTimestamp;
}
public void UpdateInstanceMaterialID(int instanceHandle, uint materialID)
{
InstanceEntry instanceEntry;
bool success = m_Instances.TryGetValue(instanceHandle, out instanceEntry);
Assert.IsTrue(success);
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
instanceInfo.userMaterialID = materialID;
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
}
public void UpdateRenderingLayerMask(int instanceHandle, uint renderingLayerMask)
{
InstanceEntry instanceEntry;
bool success = m_Instances.TryGetValue(instanceHandle, out instanceEntry);
Assert.IsTrue(success);
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
instanceInfo.renderingLayerMask = renderingLayerMask;
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
}
public void UpdateInstanceMask(int instanceHandle, uint mask)
{
bool success = m_Instances.TryGetValue(instanceHandle, out InstanceEntry instanceEntry);
Assert.IsTrue(success);
instanceEntry.instanceMask = mask;
var instanceInfo = m_InstanceBuffer.Get(instanceEntry.indexInInstanceBuffer);
instanceInfo.instanceMask = mask;
m_InstanceBuffer.Set(instanceEntry.indexInInstanceBuffer, instanceInfo);
}
public void NextFrame()
{
if ((m_FrameTimestamp - m_TransformTouchedLastTimestamp) <= 1)
{
m_InstanceBuffer.ModifyForEach(
instance =>
{
instance.previousLocalToWorld = instance.localToWorld;
return instance;
});
}
m_FrameTimestamp++;
}
public bool instanceListValid => m_InstanceBuffer != null;
public void Bind(CommandBuffer cmd, IRayTracingShader shader)
{
var gpuBuffer = m_InstanceBuffer.GetGpuBuffer(cmd);
shader.SetBufferParam(cmd, Shader.PropertyToID("g_AccelStructInstanceList"), gpuBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID("g_globalIndexBuffer"), m_GeometryPool.globalIndexBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID("g_globalVertexBuffer"), m_GeometryPool.globalVertexBuffer);
shader.SetIntParam(cmd, Shader.PropertyToID("g_globalVertexBufferStride"), m_GeometryPool.globalVertexBufferStrideBytes/4);
shader.SetBufferParam(cmd, Shader.PropertyToID("g_MeshList"), m_GeometryPool.globalMeshChunkTableEntryBuffer);
}
public int GetInstanceCount()
{
return m_Instances.Count;
}
static private float4x4 NormalMatrix(float4x4 m)
{
float3x3 t = new float3x3(m);
return new float4x4(math.inverse(math.transpose(t)), new float3(0.0));
}
readonly GeometryPool m_GeometryPool;
readonly PersistentGpuArray<RTInstance> m_InstanceBuffer = new PersistentGpuArray<RTInstance>(100);
public struct RTInstance
{
public float4x4 localToWorld;
public float4x4 previousLocalToWorld;
public float4x4 localToWorldNormals;
public uint renderingLayerMask;
public uint instanceMask;
public uint userMaterialID;
public uint geometryIndex;
};
public class InstanceEntry
{
public GeometryPoolHandle geometryPoolHandle;
public BlockAllocator.Allocation indexInInstanceBuffer;
public uint instanceMask;
public uint vertexOffset;
public uint indexOffset;
}
readonly Dictionary<int, InstanceEntry> m_Instances = new Dictionary<int, InstanceEntry>();
uint m_FrameTimestamp = 0;
uint m_TransformTouchedLastTimestamp = 0;
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/AccelStructInstances.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 655b78989e21c594c92de1023779bc0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 23232b60bf4efdb4abc21f1228d359cc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

869
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.cs

@ -0,0 +1,869 @@
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine.Assertions;
// This file is a fork of the GeometryPool used by the GPU Driven Pipeline
// TODO: remove that file and use GeometryPool v2 (written in C++)
namespace UnityEngine.Rendering.UnifiedRayTracing
{
// Initial description of geometry pool, contains memory limits to hold cluster / index & vertex data.
internal struct GeometryPoolDesc
{
public int vertexPoolByteSize;
public int indexPoolByteSize;
public int meshChunkTablesByteSize;
public static GeometryPoolDesc NewDefault()
{
return new GeometryPoolDesc()
{
vertexPoolByteSize = 256 * 1024 * 1024, //256 mb
indexPoolByteSize = 32 * 1024 * 1024, //32 mb
meshChunkTablesByteSize = 4 * 1024 * 1024
};
}
}
// Handle to a piece of geo. Geometry meshes registered are ref counted.
// Each handle allocation must be deallocated manually.
internal struct GeometryPoolHandle : IEquatable<GeometryPoolHandle>
{
public int index;
public static readonly GeometryPoolHandle Invalid = new GeometryPoolHandle() { index = -1 };
public readonly bool valid => index != -1;
public bool Equals(GeometryPoolHandle other) => index == other.index;
}
// Entry information of a geometry handle.
// Use this helper to check validity, material hashes and active refcounts.
internal struct GeometryPoolEntryInfo
{
public bool valid;
public uint refCount;
public static GeometryPoolEntryInfo NewDefault()
{
return new GeometryPoolEntryInfo()
{
valid = false,
refCount = 0
};
}
}
// Descriptor of piece of geometry (the submesh information).
internal struct GeometryPoolSubmeshData
{
public int submeshIndex;
public Material material;
}
// Description of the geometry pool entry.
// Contains master list and the submesh data information.
internal struct GeometryPoolEntryDesc
{
public Mesh mesh;
public GeometryPoolSubmeshData[] submeshData;
}
// Geometry pool container. Contains a global set of geometry accessible from the GPU.
internal sealed class GeometryPool : IDisposable
{
private const int kMaxThreadGroupsPerDispatch = 65535; // Counted in groups, not threads.
private const int kThreadGroupSize = 256; // Counted in threads
private static class GeoPoolShaderIDs
{
// MainUpdateIndexBuffer32 and MainUpdateIndexBuffer16 Kernel Strings
public static readonly int _InputIBBaseOffset = Shader.PropertyToID("_InputIBBaseOffset");
public static readonly int _DispatchIndexOffset = Shader.PropertyToID("_DispatchIndexOffset");
public static readonly int _InputIBCount = Shader.PropertyToID("_InputIBCount");
public static readonly int _OutputIBOffset = Shader.PropertyToID("_OutputIBOffset");
public static readonly int _InputFirstVertex = Shader.PropertyToID("_InputFirstVertex");
public static readonly int _InputIndexBuffer = Shader.PropertyToID("_InputIndexBuffer");
public static readonly int _OutputIndexBuffer = Shader.PropertyToID("_OutputIndexBuffer");
// MainUpdateVertexBuffer Kernel Strings
public static readonly int _InputVBCount = Shader.PropertyToID("_InputVBCount");
public static readonly int _InputBaseVertexOffset = Shader.PropertyToID("_InputBaseVertexOffset");
public static readonly int _DispatchVertexOffset = Shader.PropertyToID("_DispatchVertexOffset");
public static readonly int _OutputVBSize = Shader.PropertyToID("_OutputVBSize");
public static readonly int _OutputVBOffset = Shader.PropertyToID("_OutputVBOffset");
public static readonly int _InputPosBufferStride = Shader.PropertyToID("_InputPosBufferStride");
public static readonly int _InputPosBufferOffset = Shader.PropertyToID("_InputPosBufferOffset");
public static readonly int _InputUv0BufferStride = Shader.PropertyToID("_InputUv0BufferStride");
public static readonly int _InputUv0BufferOffset = Shader.PropertyToID("_InputUv0BufferOffset");
public static readonly int _InputUv1BufferStride = Shader.PropertyToID("_InputUv1BufferStride");
public static readonly int _InputUv1BufferOffset = Shader.PropertyToID("_InputUv1BufferOffset");
public static readonly int _InputNormalBufferStride = Shader.PropertyToID("_InputNormalBufferStride");
public static readonly int _InputNormalBufferOffset = Shader.PropertyToID("_InputNormalBufferOffset");
public static readonly int _PosBuffer = Shader.PropertyToID("_PosBuffer");
public static readonly int _Uv0Buffer = Shader.PropertyToID("_Uv0Buffer");
public static readonly int _Uv1Buffer = Shader.PropertyToID("_Uv1Buffer");
public static readonly int _NormalBuffer = Shader.PropertyToID("_NormalBuffer");
public static readonly int _OutputVB = Shader.PropertyToID("_OutputVB");
public static readonly int _AttributesMask = Shader.PropertyToID("_AttributesMask");
}
// Geometry slot represents a set of pointers to the blobs of vertex, index and cluster information
private const int InvalidHandle = -1;
public struct MeshChunk
{
public BlockAllocator.Allocation vertexAlloc;
public BlockAllocator.Allocation indexAlloc;
public GeoPoolMeshChunk EncodeGPUEntry()
{
return new GeoPoolMeshChunk() {
indexOffset = indexAlloc.block.offset,
indexCount = indexAlloc.block.count,
vertexOffset = vertexAlloc.block.offset,
vertexCount = vertexAlloc.block.count,
};
}
public static MeshChunk Invalid => new MeshChunk()
{
vertexAlloc = BlockAllocator.Allocation.Invalid,
indexAlloc = BlockAllocator.Allocation.Invalid
};
}
public struct GeometrySlot
{
public uint refCount;
public uint hash;
public BlockAllocator.Allocation meshChunkTableAlloc;
public NativeArray<MeshChunk> meshChunks;
public bool hasGPUData;
public static readonly GeometrySlot Invalid = new GeometrySlot()
{
meshChunkTableAlloc = BlockAllocator.Allocation.Invalid,
hasGPUData = false,
};
public bool valid => meshChunkTableAlloc.valid;
}
private struct GeoPoolEntrySlot
{
public uint refCount;
public uint hash;
public int geoSlotHandle;
public static readonly GeoPoolEntrySlot Invalid = new GeoPoolEntrySlot()
{
refCount = 0u,
hash = 0u,
geoSlotHandle = InvalidHandle,
};
public bool valid => geoSlotHandle != InvalidHandle;
}
private struct VertexBufferAttribInfo
{
public GraphicsBuffer buffer;
public int stride;
public int offset;
public int byteCount;
public bool valid => buffer != null;
}
public static int GetVertexByteSize() => GeometryPoolConstants.GeoPoolVertexByteSize;
public static int GetIndexByteSize() => GeometryPoolConstants.GeoPoolIndexByteSize;
public static int GetMeshChunkTableEntryByteSize() => System.Runtime.InteropServices.Marshal.SizeOf<GeoPoolMeshChunk>();
private int GetFormatByteCount(VertexAttributeFormat format)
{
switch (format)
{
case VertexAttributeFormat.Float32: return 4;
case VertexAttributeFormat.Float16: return 2;
case VertexAttributeFormat.UNorm8: return 1;
case VertexAttributeFormat.SNorm8: return 1;
case VertexAttributeFormat.UNorm16: return 2;
case VertexAttributeFormat.SNorm16: return 2;
case VertexAttributeFormat.UInt8: return 1;
case VertexAttributeFormat.SInt8: return 1;
case VertexAttributeFormat.UInt16: return 2;
case VertexAttributeFormat.SInt16: return 2;
case VertexAttributeFormat.UInt32: return 4;
case VertexAttributeFormat.SInt32: return 4;
}
return 4;
}
private static int DivUp(int x, int y) => (x + y - 1) / y;
private const GraphicsBuffer.Target VertexBufferTarget = GraphicsBuffer.Target.Structured;
private const GraphicsBuffer.Target IndexBufferTarget = GraphicsBuffer.Target.Structured;
public GraphicsBuffer globalIndexBuffer { get { return m_GlobalIndexBuffer; } }
public GraphicsBuffer globalVertexBuffer { get { return m_GlobalVertexBuffer; } }
public int globalVertexBufferStrideBytes { get { return GetVertexByteSize(); } }
public GraphicsBuffer globalMeshChunkTableEntryBuffer { get { return m_GlobalMeshChunkTableEntryBuffer; } }
public int indicesCount => m_MaxIndexCounts;
public int verticesCount => m_MaxVertCounts;
public int meshChunkTablesEntryCount => m_MaxMeshChunkTableEntriesCount;
GraphicsBuffer m_GlobalIndexBuffer = null;
GraphicsBuffer m_GlobalVertexBuffer = null;
GraphicsBuffer m_GlobalMeshChunkTableEntryBuffer = null;
readonly GraphicsBuffer m_DummyBuffer = null;
int m_MaxVertCounts;
int m_MaxIndexCounts;
int m_MaxMeshChunkTableEntriesCount;
BlockAllocator m_VertexAllocator;
BlockAllocator m_IndexAllocator;
BlockAllocator m_MeshChunkTableAllocator;
NativeParallelHashMap<uint, int> m_MeshHashToGeoSlot;
List<GeometrySlot> m_GeoSlots;
NativeList<int> m_FreeGeoSlots;
NativeParallelHashMap<uint, GeometryPoolHandle> m_GeoPoolEntryHashToSlot;
NativeList<GeoPoolEntrySlot> m_GeoPoolEntrySlots;
NativeList<GeometryPoolHandle> m_FreeGeoPoolEntrySlots;
readonly List<GraphicsBuffer> m_InputBufferReferences;
readonly ComputeShader m_CopyShader;
ComputeShader m_GeometryPoolKernelsCS;
int m_KernelMainUpdateIndexBuffer16;
int m_KernelMainUpdateIndexBuffer32;
int m_KernelMainUpdateVertexBuffer;
readonly CommandBuffer m_CmdBuffer;
bool m_MustClearCmdBuffer;
int m_PendingCmds;
public GeometryPool(in GeometryPoolDesc desc, ComputeShader geometryPoolShader, ComputeShader copyShader)
{
m_CopyShader = copyShader;
LoadKernels(geometryPoolShader);
m_CmdBuffer = new CommandBuffer();
m_InputBufferReferences = new List<GraphicsBuffer>();
m_MustClearCmdBuffer = false;
m_PendingCmds = 0;
m_MaxVertCounts = CalcVertexCount(desc.vertexPoolByteSize);
m_MaxIndexCounts = CalcIndexCount(desc.indexPoolByteSize);
m_MaxMeshChunkTableEntriesCount = CalcMeshChunkTablesCount(desc.meshChunkTablesByteSize);
m_GlobalVertexBuffer = new GraphicsBuffer(VertexBufferTarget, DivUp(m_MaxVertCounts * GetVertexByteSize(), 4), 4);
m_GlobalIndexBuffer = new GraphicsBuffer(IndexBufferTarget, m_MaxIndexCounts, 4);
m_GlobalMeshChunkTableEntryBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, m_MaxMeshChunkTableEntriesCount, GetMeshChunkTableEntryByteSize());
m_DummyBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 16, 4);
var initialCapacity = 4096;
m_MeshHashToGeoSlot = new NativeParallelHashMap<uint, int>(initialCapacity, Allocator.Persistent);
m_GeoSlots = new List<GeometrySlot>();
m_FreeGeoSlots = new NativeList<int>(Allocator.Persistent);
m_GeoPoolEntryHashToSlot = new NativeParallelHashMap<uint, GeometryPoolHandle>(initialCapacity, Allocator.Persistent);
m_GeoPoolEntrySlots = new NativeList<GeoPoolEntrySlot>(Allocator.Persistent);
m_FreeGeoPoolEntrySlots = new NativeList<GeometryPoolHandle>(Allocator.Persistent);
m_VertexAllocator = new BlockAllocator();
m_VertexAllocator.Initialize(m_MaxVertCounts);
m_IndexAllocator = new BlockAllocator();
m_IndexAllocator.Initialize(m_MaxIndexCounts);
m_MeshChunkTableAllocator = new BlockAllocator();
m_MeshChunkTableAllocator.Initialize(m_MaxMeshChunkTableEntriesCount);
}
void DisposeInputBuffers()
{
if (m_InputBufferReferences.Count == 0)
return;
foreach (var b in m_InputBufferReferences)
b.Dispose();
m_InputBufferReferences.Clear();
}
public void Dispose()
{
m_IndexAllocator.Dispose();
m_VertexAllocator.Dispose();
m_MeshChunkTableAllocator.Dispose();
m_DummyBuffer.Dispose();
m_MeshHashToGeoSlot.Dispose();
foreach (var geoSlot in m_GeoSlots)
{
if (geoSlot.valid)
geoSlot.meshChunks.Dispose();
}
m_GeoSlots = null;
m_FreeGeoSlots.Dispose();
m_GeoPoolEntryHashToSlot.Dispose();
m_GeoPoolEntrySlots.Dispose();
m_FreeGeoPoolEntrySlots.Dispose();
m_GlobalIndexBuffer.Dispose();
m_GlobalVertexBuffer.Release();
m_GlobalMeshChunkTableEntryBuffer.Dispose();
m_CmdBuffer.Release();
DisposeInputBuffers();
}
private void LoadKernels(ComputeShader geometryPoolShader)
{
m_GeometryPoolKernelsCS = geometryPoolShader;
m_KernelMainUpdateIndexBuffer16 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer16");
m_KernelMainUpdateIndexBuffer32 = m_GeometryPoolKernelsCS.FindKernel("MainUpdateIndexBuffer32");
m_KernelMainUpdateVertexBuffer = m_GeometryPoolKernelsCS.FindKernel("MainUpdateVertexBuffer");
}
private int CalcVertexCount(int bufferByteSize) => DivUp(bufferByteSize, GetVertexByteSize());
private int CalcIndexCount(int bufferByteSize) => DivUp(bufferByteSize, GetIndexByteSize());
private int CalcMeshChunkTablesCount(int bufferByteSize) => DivUp(bufferByteSize, GetMeshChunkTableEntryByteSize());
private void DeallocateGeometrySlot(ref GeometrySlot slot)
{
if (slot.meshChunkTableAlloc.valid)
{
m_MeshChunkTableAllocator.FreeAllocation(slot.meshChunkTableAlloc);
if (slot.meshChunks.IsCreated)
{
for (int i = 0; i < slot.meshChunks.Length; ++i)
{
var meshChunk = slot.meshChunks[i];
if (meshChunk.vertexAlloc.valid)
m_VertexAllocator.FreeAllocation(meshChunk.vertexAlloc);
if (meshChunk.indexAlloc.valid)
m_IndexAllocator.FreeAllocation(meshChunk.indexAlloc);
}
slot.meshChunks.Dispose();
}
}
slot = GeometrySlot.Invalid;
}
private void DeallocateGeometrySlot(int geoSlotHandle)
{
var geoSlot = m_GeoSlots[geoSlotHandle];
Assertions.Assert.IsTrue(geoSlot.valid);
--geoSlot.refCount;
if (geoSlot.refCount == 0)
{
m_MeshHashToGeoSlot.Remove(geoSlot.hash);
DeallocateGeometrySlot(ref geoSlot);
m_FreeGeoSlots.Add(geoSlotHandle);
}
m_GeoSlots[geoSlotHandle] = geoSlot;
}
private bool AllocateGeo(Mesh mesh, out int allocationHandle)
{
uint meshHash = (uint)mesh.GetHashCode();
int indexCount = 0;
for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
{
indexCount += (int)mesh.GetIndexCount(submeshIndex);
}
if (m_MeshHashToGeoSlot.TryGetValue(meshHash, out allocationHandle))
{
var geoSlot = m_GeoSlots[allocationHandle];
Assertions.Assert.IsTrue(geoSlot.hash == meshHash);
Assertions.Assert.IsTrue(geoSlot.meshChunkTableAlloc.block.count == mesh.subMeshCount);
++geoSlot.refCount;
m_GeoSlots[allocationHandle] = geoSlot;
return true;
}
allocationHandle = InvalidHandle;
var newSlot = GeometrySlot.Invalid;
newSlot.refCount = 1;
newSlot.hash = meshHash;
bool allocationSuccess = true;
if (mesh.subMeshCount > 0)
{
newSlot.meshChunkTableAlloc = m_MeshChunkTableAllocator.Allocate(mesh.subMeshCount);
if (!newSlot.meshChunkTableAlloc.valid)
{
newSlot.meshChunkTableAlloc = m_MeshChunkTableAllocator.GrowAndAllocate(mesh.subMeshCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / GetMeshChunkTableEntryByteSize(), out int oldCapacity, out int newCapacity);
if (!newSlot.meshChunkTableAlloc.valid)
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, GetMeshChunkTableEntryByteSize(), ref m_GlobalMeshChunkTableEntryBuffer);
m_MaxMeshChunkTableEntriesCount = newCapacity;
}
newSlot.meshChunks = new NativeArray<MeshChunk>(mesh.subMeshCount, Allocator.Persistent);
for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
{
SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(submeshIndex);
var newMeshChunk = MeshChunk.Invalid;
newMeshChunk.vertexAlloc = m_VertexAllocator.Allocate(submeshDescriptor.vertexCount);
if (!newMeshChunk.vertexAlloc.valid)
{
newMeshChunk.vertexAlloc = m_VertexAllocator.GrowAndAllocate(submeshDescriptor.vertexCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / GetVertexByteSize(), out int oldCapacity, out int newCapacity);
if (!newMeshChunk.vertexAlloc.valid)
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, GetVertexByteSize(), ref m_GlobalVertexBuffer);
m_MaxVertCounts = newCapacity;
}
newMeshChunk.indexAlloc = m_IndexAllocator.Allocate(submeshDescriptor.indexCount);
if (!newMeshChunk.indexAlloc.valid)
{
newMeshChunk.indexAlloc = m_IndexAllocator.GrowAndAllocate(submeshDescriptor.indexCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / sizeof(int), out int oldCapacity, out int newCapacity);
if (!newMeshChunk.indexAlloc.valid)
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, sizeof(int), ref m_GlobalIndexBuffer);
m_MaxIndexCounts = newCapacity;
}
newSlot.meshChunks[submeshIndex] = newMeshChunk;
}
}
if (!allocationSuccess)
{
DeallocateGeometrySlot(ref newSlot);
return false;
}
if (m_FreeGeoSlots.IsEmpty)
{
allocationHandle = m_GeoSlots.Count;
m_GeoSlots.Add(newSlot);
}
else
{
allocationHandle = m_FreeGeoSlots[m_FreeGeoSlots.Length - 1];
m_FreeGeoSlots.RemoveAtSwapBack(m_FreeGeoSlots.Length - 1);
Assertions.Assert.IsTrue(!m_GeoSlots[allocationHandle].valid);
m_GeoSlots[allocationHandle] = newSlot;
}
m_MeshHashToGeoSlot.Add(newSlot.hash, allocationHandle);
return true;
}
private void DeallocateGeoPoolEntrySlot(GeometryPoolHandle handle)
{
var slot = m_GeoPoolEntrySlots[handle.index];
--slot.refCount;
if (slot.refCount == 0)
{
m_GeoPoolEntryHashToSlot.Remove(slot.hash);
DeallocateGeoPoolEntrySlot(ref slot);
m_FreeGeoPoolEntrySlots.Add(handle);
}
m_GeoPoolEntrySlots[handle.index] = slot;
}
private void DeallocateGeoPoolEntrySlot(ref GeoPoolEntrySlot geoPoolEntrySlot)
{
if (geoPoolEntrySlot.geoSlotHandle != InvalidHandle)
DeallocateGeometrySlot(geoPoolEntrySlot.geoSlotHandle);
geoPoolEntrySlot = GeoPoolEntrySlot.Invalid;
}
public GeometryPoolEntryInfo GetEntryInfo(GeometryPoolHandle handle)
{
if (!handle.valid)
return GeometryPoolEntryInfo.NewDefault();
GeoPoolEntrySlot slot = m_GeoPoolEntrySlots[handle.index];
if (!slot.valid)
return GeometryPoolEntryInfo.NewDefault();
if (slot.geoSlotHandle == -1)
Debug.LogErrorFormat("Found invalid geometry slot handle with handle id {0}.", handle.index);
return new GeometryPoolEntryInfo()
{
valid = slot.valid,
refCount = slot.refCount
};
}
public GeometrySlot GetEntryGeomAllocation(GeometryPoolHandle handle)
{
var slot = m_GeoPoolEntrySlots[handle.index];
Assertions.Assert.IsTrue(slot.valid);
var geoSlot = m_GeoSlots[slot.geoSlotHandle];
Assertions.Assert.IsTrue(geoSlot.valid);
return geoSlot;
}
public int GetInstanceGeometryIndex(Mesh mesh)
{
return GetEntryGeomAllocation(GetHandle(mesh)).meshChunkTableAlloc.block.offset;
}
private void UpdateGeoGpuState(Mesh mesh, GeometryPoolHandle handle)
{
var entrySlot = m_GeoPoolEntrySlots[handle.index];
var geoSlot = m_GeoSlots[entrySlot.geoSlotHandle];
CommandBuffer cmdBuffer = AllocateCommandBuffer(); //clear any previous cmd buffers.
//Upload mesh information.
if (!geoSlot.hasGPUData)
{
//Load index buffer
GraphicsBuffer buffer = LoadIndexBuffer(mesh);
Assertions.Assert.IsTrue((buffer.target & GraphicsBuffer.Target.Raw) != 0);
// Load attribute buffers
VertexBufferAttribInfo posAttrib;
LoadVertexAttribInfo(mesh, VertexAttribute.Position, out posAttrib);
VertexBufferAttribInfo uv0Attrib;
LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord0, out uv0Attrib);
VertexBufferAttribInfo uv1Attrib;
LoadVertexAttribInfo(mesh, VertexAttribute.TexCoord1, out uv1Attrib);
VertexBufferAttribInfo normalAttrib;
LoadVertexAttribInfo(mesh, VertexAttribute.Normal, out normalAttrib);
var meshChunkAllocationTable = new NativeArray<GeoPoolMeshChunk>(geoSlot.meshChunks.Length, Allocator.Temp);
for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
{
SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(submeshIndex);
MeshChunk targetMeshChunk = geoSlot.meshChunks[submeshIndex];
//Update mesh chunk vertex offset
AddVertexUpdateCommand(
cmdBuffer, submeshDescriptor.baseVertex + submeshDescriptor.firstVertex,
posAttrib, uv0Attrib, uv1Attrib, normalAttrib,
targetMeshChunk.vertexAlloc, m_GlobalVertexBuffer);
//Update mesh chunk index offset
AddIndexUpdateCommand(
cmdBuffer,
mesh.indexFormat, buffer, targetMeshChunk.indexAlloc, submeshDescriptor.firstVertex,
submeshDescriptor.indexStart, submeshDescriptor.indexCount, 0,
m_GlobalIndexBuffer);
meshChunkAllocationTable[submeshIndex] = targetMeshChunk.EncodeGPUEntry();
}
cmdBuffer.SetBufferData(m_GlobalMeshChunkTableEntryBuffer, meshChunkAllocationTable, 0, geoSlot.meshChunkTableAlloc.block.offset, meshChunkAllocationTable.Length);
meshChunkAllocationTable.Dispose();
geoSlot.hasGPUData = true;
m_GeoSlots[entrySlot.geoSlotHandle] = geoSlot;
}
}
private uint FNVHash(uint prevHash, uint dword)
{
//https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
const uint fnvPrime = 0x811C9DC5;
for (int i = 0; i < 4; ++i)
{
prevHash ^= ((dword >> (i * 8)) & 0xFF);
prevHash *= fnvPrime;
}
return prevHash;
}
private uint CalculateClusterHash(Mesh mesh, GeometryPoolSubmeshData[] submeshData)
{
uint meshHash = (uint)mesh.GetHashCode();
uint clusterHash = meshHash;
if (submeshData != null)
{
foreach (var data in submeshData)
{
clusterHash = FNVHash(clusterHash, (uint)data.submeshIndex);
clusterHash = FNVHash(clusterHash, (uint)(data.material == null ? 0 : data.material.GetHashCode()));
}
}
return clusterHash;
}
public GeometryPoolHandle GetHandle(Mesh mesh)
{
uint geoPoolEntryHash = CalculateClusterHash(mesh, null);
if (m_GeoPoolEntryHashToSlot.TryGetValue(geoPoolEntryHash, out GeometryPoolHandle outHandle))
return outHandle;
else
return GeometryPoolHandle.Invalid;
}
private static int FindSubmeshEntryInDesc(int submeshIndex, in GeometryPoolSubmeshData[] submeshData)
{
if (submeshData == null)
return -1;
for (int i = 0; i < submeshData.Length; ++i)
{
if (submeshData[i].submeshIndex == submeshIndex)
return i;
}
return -1;
}
public bool Register(Mesh mesh, out GeometryPoolHandle outHandle)
{
return Register(new GeometryPoolEntryDesc()
{
mesh = mesh,
submeshData = null
}, out outHandle);
}
public bool Register(in GeometryPoolEntryDesc entryDesc, out GeometryPoolHandle outHandle)
{
outHandle = GeometryPoolHandle.Invalid;
if (entryDesc.mesh == null)
{
return false;
}
Mesh mesh = entryDesc.mesh;
uint geoPoolEntryHash = CalculateClusterHash(entryDesc.mesh, entryDesc.submeshData);
if (m_GeoPoolEntryHashToSlot.TryGetValue(geoPoolEntryHash, out outHandle))
{
GeoPoolEntrySlot geoPoolEntrySlot = m_GeoPoolEntrySlots[outHandle.index];
Assertions.Assert.IsTrue(geoPoolEntrySlot.hash == geoPoolEntryHash);
GeometrySlot geoSlot = m_GeoSlots[geoPoolEntrySlot.geoSlotHandle];
Assertions.Assert.IsTrue(geoSlot.hash == (uint)mesh.GetHashCode());
++geoPoolEntrySlot.refCount;
m_GeoPoolEntrySlots[outHandle.index] = geoPoolEntrySlot;
return true;
}
var newSlot = GeoPoolEntrySlot.Invalid;
newSlot.refCount = 1;
newSlot.hash = geoPoolEntryHash;
// Validate submesh information
var validSubmeshData = new List<GeometryPoolSubmeshData>(mesh.subMeshCount);
if (mesh.subMeshCount > 0 && entryDesc.submeshData != null)
{
for (int submeshIndex = 0; submeshIndex < mesh.subMeshCount; ++submeshIndex)
{
int entryIndex = FindSubmeshEntryInDesc(submeshIndex, entryDesc.submeshData);
if (entryIndex == -1)
{
Debug.LogErrorFormat("Could not find submesh index {0} for mesh entry descriptor of mesh {1}.", submeshIndex, mesh.name);
continue;
}
validSubmeshData.Add(entryDesc.submeshData[entryIndex]);
}
}
if (!AllocateGeo(mesh, out newSlot.geoSlotHandle))
{
DeallocateGeoPoolEntrySlot(ref newSlot);
return false;
}
if (m_FreeGeoPoolEntrySlots.IsEmpty)
{
outHandle = new GeometryPoolHandle() { index = m_GeoPoolEntrySlots.Length };
m_GeoPoolEntrySlots.Add(newSlot);
}
else
{
outHandle = m_FreeGeoPoolEntrySlots[m_FreeGeoPoolEntrySlots.Length - 1];
m_FreeGeoPoolEntrySlots.RemoveAtSwapBack(m_FreeGeoPoolEntrySlots.Length - 1);
Assertions.Assert.IsTrue(!m_GeoPoolEntrySlots[outHandle.index].valid);
m_GeoPoolEntrySlots[outHandle.index] = newSlot;
}
m_GeoPoolEntryHashToSlot.Add(newSlot.hash, outHandle);
UpdateGeoGpuState(mesh, outHandle);
return true;
}
public void Unregister(GeometryPoolHandle handle)
{
var slot = m_GeoPoolEntrySlots[handle.index];
Assertions.Assert.IsTrue(slot.valid);
DeallocateGeoPoolEntrySlot(handle);
}
public void SendGpuCommands()
{
if (m_PendingCmds != 0)
{
Graphics.ExecuteCommandBuffer(m_CmdBuffer);
m_MustClearCmdBuffer = true;
m_PendingCmds = 0;
}
DisposeInputBuffers();
}
private GraphicsBuffer LoadIndexBuffer(Mesh mesh)
{
Debug.Assert((mesh.indexBufferTarget & GraphicsBuffer.Target.Raw) != 0 || (mesh.GetIndices(0) != null && mesh.GetIndices(0).Length != 0),
"Cant use a mesh buffer that is not raw and has no CPU index information.");
mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
var idxBuffer = mesh.GetIndexBuffer();
m_InputBufferReferences.Add(idxBuffer);
return idxBuffer;
}
void LoadVertexAttribInfo(Mesh mesh, VertexAttribute attribute, out VertexBufferAttribInfo output)
{
if (!mesh.HasVertexAttribute(attribute))
{
output.buffer = null;
output.stride = output.offset = output.byteCount = 0;
return;
}
int stream = mesh.GetVertexAttributeStream(attribute);
output.stride = mesh.GetVertexBufferStride(stream);
output.offset = mesh.GetVertexAttributeOffset(attribute);
output.byteCount = GetFormatByteCount(mesh.GetVertexAttributeFormat(attribute)) * mesh.GetVertexAttributeDimension(attribute);
output.buffer = mesh.GetVertexBuffer(stream);
m_InputBufferReferences.Add(output.buffer);
Assertions.Assert.IsTrue((output.buffer.target & GraphicsBuffer.Target.Raw) != 0);
}
private CommandBuffer AllocateCommandBuffer()
{
if (m_MustClearCmdBuffer)
{
m_CmdBuffer.Clear();
m_MustClearCmdBuffer = false;
}
++m_PendingCmds;
return m_CmdBuffer;
}
private void AddIndexUpdateCommand(
CommandBuffer cmdBuffer,
IndexFormat inputFormat,
in GraphicsBuffer inputBuffer,
in BlockAllocator.Allocation location,
int firstVertex,
int inputOffset, int indexCount, int outputOffset,
GraphicsBuffer outputIdxBuffer)
{
if (location.block.count == 0)
return;
Assertions.Assert.IsTrue(indexCount <= location.block.count);
Assertions.Assert.IsTrue(outputOffset < location.block.count);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputIBBaseOffset, inputOffset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputIBCount, indexCount);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputFirstVertex, firstVertex);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputIBOffset, location.block.offset + outputOffset);
int kernel = inputFormat == IndexFormat.UInt16 ? m_KernelMainUpdateIndexBuffer16 : m_KernelMainUpdateIndexBuffer32;
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._InputIndexBuffer, inputBuffer);
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputIndexBuffer, outputIdxBuffer);
int totalGroupCount = DivUp(location.block.count, kThreadGroupSize);
int dispatchCount = DivUp(totalGroupCount, kMaxThreadGroupsPerDispatch);
for (int dispatchIndex = 0; dispatchIndex < dispatchCount; ++dispatchIndex)
{
int indexOffset = dispatchIndex * kMaxThreadGroupsPerDispatch * kThreadGroupSize;
int dispatchGroupCount = Math.Min(kMaxThreadGroupsPerDispatch, totalGroupCount - dispatchIndex * kMaxThreadGroupsPerDispatch);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._DispatchIndexOffset, indexOffset);
cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, dispatchGroupCount, 1, 1);
}
}
private void AddVertexUpdateCommand(
CommandBuffer cmdBuffer, int baseVertexOffset,
in VertexBufferAttribInfo pos, in VertexBufferAttribInfo uv0, in VertexBufferAttribInfo uv1, in VertexBufferAttribInfo n,
in BlockAllocator.Allocation location,
GraphicsBuffer outputVertexBuffer)
{
if (location.block.count == 0)
return;
GeoPoolVertexAttribs attributes = 0;
if (pos.valid)
attributes |= GeoPoolVertexAttribs.Position;
if (uv0.valid)
attributes |= GeoPoolVertexAttribs.Uv0;
if (uv1.valid)
attributes |= GeoPoolVertexAttribs.Uv1;
if (n.valid)
attributes |= GeoPoolVertexAttribs.Normal;
int vertexCount = location.block.count;
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputVBCount, vertexCount);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputBaseVertexOffset, baseVertexOffset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBSize, m_MaxVertCounts);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._OutputVBOffset, location.block.offset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferStride, pos.stride);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputPosBufferOffset, pos.offset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferStride, uv0.stride);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv0BufferOffset, uv0.offset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferStride, uv1.stride);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputUv1BufferOffset, uv1.offset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferStride, n.stride);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._InputNormalBufferOffset, n.offset);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._AttributesMask, (int)attributes);
int kernel = m_KernelMainUpdateVertexBuffer;
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._PosBuffer, pos.valid ? pos.buffer : m_DummyBuffer);
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv0Buffer, uv0.valid ? uv0.buffer : m_DummyBuffer);
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._Uv1Buffer, uv1.valid ? uv1.buffer : m_DummyBuffer);
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._NormalBuffer, n.valid ? n.buffer : m_DummyBuffer);
cmdBuffer.SetComputeBufferParam(m_GeometryPoolKernelsCS, kernel, GeoPoolShaderIDs._OutputVB, outputVertexBuffer);
int totalGroupCount = DivUp(vertexCount, kThreadGroupSize);
int dispatchCount = DivUp(totalGroupCount, kMaxThreadGroupsPerDispatch);
for (int dispatchIndex = 0; dispatchIndex < dispatchCount; ++dispatchIndex)
{
int vertexOffset = dispatchIndex * kMaxThreadGroupsPerDispatch * kThreadGroupSize;
int dispatchGroupCount = Math.Min(kMaxThreadGroupsPerDispatch, totalGroupCount - dispatchIndex * kMaxThreadGroupsPerDispatch);
cmdBuffer.SetComputeIntParam(m_GeometryPoolKernelsCS, GeoPoolShaderIDs._DispatchVertexOffset, vertexOffset);
cmdBuffer.DispatchCompute(m_GeometryPoolKernelsCS, kernel, dispatchGroupCount, 1, 1);
}
}
}
}

2
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: df67d0cace6bd8442a1f2d5dcd0ad447

113
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.hlsl

@ -0,0 +1,113 @@
#ifndef GEOMETRY_POOL_H
#define GEOMETRY_POOL_H
namespace GeometryPool
{
float2 msign(float2 v)
{
return float2(
(v.x >= 0.0) ? 1.0 : -1.0,
(v.y >= 0.0) ? 1.0 : -1.0);
}
uint NormalToOctahedral32(float3 normal)
{
normal.xy /= (abs(normal.x) + abs(normal.y) + abs(normal.z));
normal.xy = (normal.z >= 0.0) ? normal.xy : (1.0 - abs(normal.yx)) * msign(normal.xy);
uint2 d = uint2(round(32767.5 + normal.xy * 32767.5));
return d.x | (d.y << 16u);
}
float3 Octahedral32ToNormal(uint data)
{
uint2 iv = uint2(data, data >> 16u) & 65535u;
float2 v = float2(iv) / 32767.5 - 1.0;
float3 normal = float3(v, 1.0 - abs(v.x) - abs(v.y));
float t = max(-normal.z, 0.0);
normal.x += (normal.x > 0.0) ? -t : t;
normal.y += (normal.y > 0.0) ? -t : t;
return normalize(normal);
}
uint UvsToUint32(float2 uv)
{
return (uint(uv.x * 65535.0f) & 0xFFFF) | (uint(uv.y * 65535.0f) << 16);
}
float2 Uint32ToUvs(uint data)
{
return float2((data & 0xFFFF) * (1.0f/65535.0f), (data >> 16u) * (1.0f/65535.0f));
}
void StoreUvs(RWStructuredBuffer<uint> output, uint index, float2 uv)
{
#ifdef GEOMETRY_POOL_USE_COMPRESSED_UVS
output[index] = UvsToUint32(uv);
#else
output[index] = asuint(uv.x);
output[index + 1] = asuint(uv.y);
#endif
}
float2 LoadUvs(StructuredBuffer<uint> vertexBuffer, uint index)
{
#ifdef GEOMETRY_POOL_USE_COMPRESSED_UVS
return Uint32ToUvs(vertexBuffer[index]);
#else
return asfloat(uint2(vertexBuffer[index], vertexBuffer[index + 1]));
#endif
}
void StoreVertex(
uint vertexIndex,
in GeoPoolVertex vertex,
int outputBufferSize,
RWStructuredBuffer<uint> output)
{
uint posIndex = vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE / 4;
output[posIndex] = asuint(vertex.pos.x);
output[posIndex+1] = asuint(vertex.pos.y);
output[posIndex+2] = asuint(vertex.pos.z);
uint uv0Index = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_UV0BYTE_OFFSET) / 4;
StoreUvs(output, uv0Index, vertex.uv0);
uint uv1Index = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_UV1BYTE_OFFSET) / 4;
StoreUvs(output, uv1Index, vertex.uv1);
uint normalIndex = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_NORMAL_BYTE_OFFSET) / 4;
output[normalIndex] = NormalToOctahedral32(vertex.N);
}
void LoadVertex(
uint vertexIndex,
int vertexFlags,
StructuredBuffer<uint> vertexBuffer,
out GeoPoolVertex outputVertex)
{
uint posIndex = vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE / 4;
float3 pos = asfloat(uint3(vertexBuffer[posIndex], vertexBuffer[posIndex + 1], vertexBuffer[posIndex + 2]));
uint uv0Index = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_UV0BYTE_OFFSET) / 4;
float2 uv0 = LoadUvs(vertexBuffer, uv0Index);
uint uv1Index = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_UV1BYTE_OFFSET) / 4;
float2 uv1 = LoadUvs(vertexBuffer, uv1Index);
uint normalIndex = (vertexIndex * GEO_POOL_VERTEX_BYTE_SIZE + GEO_POOL_NORMAL_BYTE_OFFSET) / 4;
uint normal = uint(vertexBuffer[normalIndex]);
outputVertex.pos = pos;
outputVertex.uv0 = uv0;
outputVertex.uv1 = uv1;
outputVertex.N = Octahedral32ToNormal(normal);
}
}
#endif

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPool.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 48162143dc3e410080eca2efcb84cb58
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

40
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs

@ -0,0 +1,40 @@
using System;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class GeometryPoolConstants
{
public const int GeoPoolPosByteSize = 3 * 4;
const int UvFieldSizeInDWords = 2;
public const int GeoPoolUV0ByteSize = UvFieldSizeInDWords * 4;
public const int GeoPoolUV1ByteSize = UvFieldSizeInDWords * 4;
public const int GeoPoolNormalByteSize = 1 * 4;
public const int GeoPoolPosByteOffset = 0;
public const int GeoPoolUV0ByteOffset = GeoPoolPosByteOffset + GeoPoolPosByteSize;
public const int GeoPoolUV1ByteOffset = GeoPoolUV0ByteOffset + GeoPoolUV0ByteSize;
public const int GeoPoolNormalByteOffset = GeoPoolUV1ByteOffset + GeoPoolUV1ByteSize;
public const int GeoPoolIndexByteSize = 4;
public const int GeoPoolVertexByteSize = GeoPoolPosByteSize + GeoPoolUV0ByteSize + GeoPoolUV1ByteSize + GeoPoolNormalByteSize;
}
internal struct GeoPoolVertex
{
public Vector3 pos;
public Vector2 uv0;
public Vector2 uv1;
public Vector3 N;
}
internal struct GeoPoolMeshChunk
{
public int indexOffset;
public int indexCount;
public int vertexOffset;
public int vertexCount;
}
[Flags]
internal enum GeoPoolVertexAttribs { Position = 1, Normal = 2, Uv0 = 4, Uv1 = 8 }
}

53
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.hlsl

@ -0,0 +1,53 @@
#ifndef GEOMETRYPOOLDEFS_CS_HLSL
#define GEOMETRYPOOLDEFS_CS_HLSL
//#define GEOMETRY_POOL_USE_COMPRESSED_UVS
//
// UnityEngine.Rendering.UnifiedRayTracing.GeoPoolVertexAttribs: static fields
//
#define GEOPOOLVERTEXATTRIBS_POSITION (1)
#define GEOPOOLVERTEXATTRIBS_NORMAL (2)
#define GEOPOOLVERTEXATTRIBS_UV0 (4)
#define GEOPOOLVERTEXATTRIBS_UV1 (8)
//
// UnityEngine.Rendering.UnifiedRayTracing.GeometryPoolConstants: static fields
//
#define GEO_POOL_POS_BYTE_SIZE (12)
#ifdef GEOMETRY_POOL_USE_COMPRESSED_UVS
#define GEO_POOL_UV0BYTE_SIZE (4)
#define GEO_POOL_UV1BYTE_SIZE (4)
#else
#define GEO_POOL_UV0BYTE_SIZE (8)
#define GEO_POOL_UV1BYTE_SIZE (8)
#endif
#define GEO_POOL_NORMAL_BYTE_SIZE (4)
#define GEO_POOL_POS_BYTE_OFFSET (0)
#define GEO_POOL_UV0BYTE_OFFSET (GEO_POOL_POS_BYTE_SIZE)
#define GEO_POOL_UV1BYTE_OFFSET (GEO_POOL_POS_BYTE_SIZE+GEO_POOL_UV0BYTE_SIZE)
#define GEO_POOL_NORMAL_BYTE_OFFSET (GEO_POOL_POS_BYTE_SIZE+GEO_POOL_UV0BYTE_SIZE+GEO_POOL_UV1BYTE_SIZE)
#define GEO_POOL_INDEX_BYTE_SIZE (4)
#define GEO_POOL_VERTEX_BYTE_SIZE (GEO_POOL_NORMAL_BYTE_OFFSET+GEO_POOL_NORMAL_BYTE_SIZE)
// Generated from UnityEngine.Rendering.UnifiedRayTracing.GeoPoolMeshChunk
struct GeoPoolMeshChunk
{
int indexOffset;
int indexCount;
int vertexOffset;
int vertexCount;
};
// Generated from UnityEngine.Rendering.UnifiedRayTracing.GeoPoolVertex
struct GeoPoolVertex
{
float3 pos;
float2 uv0;
float2 uv1;
float3 N;
};
#endif

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 60aab5cf6c3946d438e6483635c83a36
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

2
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolDefs.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ea70f29121a32094584be1fe3cf064dc

106
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolKernels.compute

@ -0,0 +1,106 @@
#pragma kernel MainUpdateIndexBuffer16
#pragma kernel MainUpdateIndexBuffer32
#pragma kernel MainUpdateVertexBuffer
#include "GeometryPoolDefs.cs.hlsl"
#include "GeometryPool.hlsl"
#define GROUP_SIZE_X 256
#define GROUP_SIZE_X_HALF (GROUP_SIZE_X >> 1)
int _DispatchIndexOffset;
int _InputIBBaseOffset;
int _InputIBCount;
int _InputFirstVertex;
int _OutputIBOffset;
ByteAddressBuffer _InputIndexBuffer;
RWStructuredBuffer<uint> _OutputIndexBuffer;
[numthreads(GROUP_SIZE_X,1,1)]
void MainUpdateIndexBuffer32(uint3 dispatchThreadID : SV_DispatchThreadID, int3 groupID : SV_GroupID)
{
uint bufferOffset = _DispatchIndexOffset + dispatchThreadID.x;
if (bufferOffset >= (uint)_InputIBCount)
return;
uint indexVal = _InputIndexBuffer.Load((_InputIBBaseOffset + bufferOffset) << 2u);
_OutputIndexBuffer[(uint)_OutputIBOffset + bufferOffset] = indexVal - _InputFirstVertex;
}
groupshared uint _ldsIndexCache[GROUP_SIZE_X_HALF + 1];
[numthreads(GROUP_SIZE_X,1,1)]
void MainUpdateIndexBuffer16(uint3 dispatchThreadID : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint3 groupID : SV_GroupID)
{
if (groupIndex < (GROUP_SIZE_X_HALF + 1))
_ldsIndexCache[groupIndex] = _InputIndexBuffer.Load((GROUP_SIZE_X_HALF * (groupID.x) + groupIndex + (uint)(_InputIBBaseOffset + _DispatchIndexOffset) / 2) << 2u);
GroupMemoryBarrierWithGroupSync();
uint bufferOffset = _DispatchIndexOffset + dispatchThreadID.x;
if (bufferOffset >= (uint)_InputIBCount)
return;
uint localOffset = groupIndex + ((_InputIBBaseOffset + _DispatchIndexOffset) & 0x1);
uint pair = _ldsIndexCache[localOffset >> 1u];
uint value = (localOffset & 0x1) ? (pair >> 16) : (pair & 0xffff);
_OutputIndexBuffer[(uint)_OutputIBOffset + bufferOffset] = value - _InputFirstVertex;
}
int _InputVBCount;
int _InputBaseVertexOffset;
int _DispatchVertexOffset;
int _OutputVBSize;
int _OutputVBOffset;
int _InputPosBufferStride;
int _InputPosBufferOffset;
int _InputUv0BufferStride;
int _InputUv0BufferOffset;
int _InputUv1BufferStride;
int _InputUv1BufferOffset;
int _InputNormalBufferStride;
int _InputNormalBufferOffset;
ByteAddressBuffer _PosBuffer;
ByteAddressBuffer _Uv0Buffer;
ByteAddressBuffer _Uv1Buffer;
ByteAddressBuffer _NormalBuffer;
RWStructuredBuffer<uint> _OutputVB;
int _AttributesMask;
[numthreads(GROUP_SIZE_X, 1, 1)]
void MainUpdateVertexBuffer(uint3 dispatchThreadID : SV_DispatchThreadID)
{
int inputVertexOffset = _DispatchVertexOffset + (int)dispatchThreadID.x;
if ((int)inputVertexOffset >= _InputVBCount)
return;
inputVertexOffset += _InputBaseVertexOffset;
GeoPoolVertex vtx = (GeoPoolVertex)0;
if (_AttributesMask & GEOPOOLVERTEXATTRIBS_POSITION)
vtx.pos = asfloat(_PosBuffer.Load3(_InputPosBufferOffset + (inputVertexOffset * _InputPosBufferStride)));
if (_AttributesMask & GEOPOOLVERTEXATTRIBS_UV0)
vtx.uv0.xy = asfloat(_Uv0Buffer.Load2(_InputUv0BufferOffset + (inputVertexOffset * _InputUv0BufferStride)));
if (_AttributesMask & GEOPOOLVERTEXATTRIBS_UV1)
vtx.uv1.xy = asfloat(_Uv1Buffer.Load2(_InputUv1BufferOffset + (inputVertexOffset * _InputUv1BufferStride)));
if (_AttributesMask & GEOPOOLVERTEXATTRIBS_NORMAL)
vtx.N = asfloat(_NormalBuffer.Load3(_InputNormalBufferOffset + (inputVertexOffset * _InputNormalBufferStride)));
uint outputVertexOffset = _DispatchVertexOffset + (int)dispatchThreadID.x;
GeometryPool::StoreVertex(_OutputVBOffset + outputVertexOffset, vtx, _OutputVBSize, _OutputVB);
}

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/GeometryPool/GeometryPoolKernels.compute.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e477e231a2d044e6bc38544c4b746446
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

284
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs

@ -0,0 +1,284 @@
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class TerrainToMesh
{
static private AsyncTerrainToMeshRequest MakeAsyncTerrainToMeshRequest(int width, int height, Vector3 heightmapScale, float[,] heightmap, bool[,] holes)
{
int vertexCount = width * height;
var job = new ComputeTerrainMeshJob();
//WW1MOD This crashes when baking 4k terrains, as it tries to allocate a *2GB* Buffer!.. which is fine *here* but when it gets uploaded to GPU, it goes crashy or error, depending on dx-API/Driver
//Note heightmap size is always pow2+1
//the "else" part is the original code
if (height == 4097)
{
const int halfSize = 2049;
int _vertexCount = halfSize * halfSize;
Debug.Log("Downsizing Terrain heightmap mesh output to 2k to prevent crashes");
job.heightmap = new NativeArray<float>(_vertexCount, Allocator.Persistent);
for (int i = 0; i < _vertexCount; ++i)
{
float h0 = heightmap[((i*2)+0) / (width), ((i*2)+0) % (width)];
float h1 = heightmap[((i*2)+1) / (width), ((i*2)+0) % (width)];
float h2 = heightmap[((i*2)+0) / (width), ((i*2)+1) % (width)];
float h3 = heightmap[((i*2)+1) / (width), ((i*2)+1) % (width)];
job.heightmap[i] = (h0 + h1 + h2 + h3) / 4f;
}
job.holes = new NativeArray<bool>((halfSize - 1) * (halfSize - 1), Allocator.Persistent);
for (int i = 0; i < (halfSize - 1) * (halfSize - 1); ++i)
{
bool h0 = holes[((i*2)+0) / (width - 1), ((i*2)+0) % (width - 1)];
bool h1 = holes[((i*2)+1) / (width - 1), ((i*2)+0) % (width - 1)];
bool h2 = holes[((i*2)+0) / (width - 1), ((i*2)+1) % (width - 1)];
bool h3 = holes[((i*2)+1) / (width - 1), ((i*2)+1) % (width - 1)];
job.holes[i] = h0 || h1 || h2 || h3;
}
height = width = halfSize;
vertexCount = _vertexCount;
}
else
{
job.heightmap = new NativeArray<float>(vertexCount, Allocator.Persistent);
for (int i = 0; i < vertexCount; ++i)
job.heightmap[i] = heightmap[i / (width), i % (width)];
job.holes = new NativeArray<bool>((width - 1) * (height - 1), Allocator.Persistent);
for (int i = 0; i < (width - 1) * (height - 1); ++i)
job.holes[i] = holes[i / (width - 1), i % (width - 1)];
}
job.width = width;
job.height = height;
job.heightmapScale = heightmapScale;
job.positions = new NativeArray<float3>(vertexCount, Allocator.Persistent);
job.uvs = new NativeArray<float2>(vertexCount, Allocator.Persistent);
job.normals = new NativeArray<float3>(vertexCount, Allocator.Persistent);
job.indices = new NativeArray<int>((width - 1) * (height - 1) * 6, Allocator.Persistent);
JobHandle jobHandle = job.Schedule(vertexCount, math.max(width, 128));
return new AsyncTerrainToMeshRequest(job, jobHandle);
}
static public AsyncTerrainToMeshRequest ConvertAsync(Terrain terrain)
{
TerrainData terrainData = terrain.terrainData;
int width = terrainData.heightmapTexture.width;
int height = terrainData.heightmapTexture.height;
float[,] heightmap = terrain.terrainData.GetHeights(0, 0, width, height);
bool[,] holes = terrain.terrainData.GetHoles(0, 0, width - 1, height - 1);
return MakeAsyncTerrainToMeshRequest(width, height, terrainData.heightmapScale, heightmap, holes);
}
static public AsyncTerrainToMeshRequest ConvertAsync(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)
{
float[,] heightmap = new float[heightmapWidth,heightmapHeight];
for (int y = 0; y < heightmapHeight; ++y)
for (int x = 0; x < heightmapWidth; ++x)
heightmap[y, x] = (float)heightmapData[y * heightmapWidth + x] / (float)32766;
bool[,] holes = new bool[heightmapWidth - 1, heightmapHeight - 1];
if (holedata != null)
{
for (int y = 0; y < heightmapHeight - 1; ++y)
for (int x = 0; x < heightmapWidth - 1; ++x)
holes[y, x] = holedata[y * holeWidth + x] != 0;
}
else
{
for (int y = 0; y < heightmapHeight - 1; ++y)
for (int x = 0; x < heightmapWidth - 1; ++x)
holes[x, y] = true;
}
return MakeAsyncTerrainToMeshRequest(heightmapWidth, heightmapHeight, heightmapScale, heightmap, holes);
}
static public Mesh Convert(Terrain terrain)
{
var request = ConvertAsync(terrain);
request.WaitForCompletion();
return request.GetMesh();
}
static public Mesh Convert(int heightmapWidth, int heightmapHeight, short[] heightmapData, Vector3 heightmapScale, int holeWidth, int holeHeight, byte[] holedata)
{
var request = ConvertAsync(heightmapWidth, heightmapHeight, heightmapData, heightmapScale, holeWidth, holeHeight, holedata);
request.WaitForCompletion();
return request.GetMesh();
}
}
internal struct AsyncTerrainToMeshRequest
{
internal AsyncTerrainToMeshRequest(ComputeTerrainMeshJob job, JobHandle jobHandle)
{
m_Job = job;
m_JobHandle = jobHandle;
}
public bool done { get { return m_JobHandle.IsCompleted; } }
public Mesh GetMesh()
{
if (!done)
return null;
Mesh mesh = new Mesh();
mesh.indexFormat = IndexFormat.UInt32;
mesh.SetVertices(m_Job.positions);
mesh.SetUVs(0, m_Job.uvs);
mesh.SetNormals(m_Job.normals);
mesh.SetIndices(TriangleIndicesWithoutHoles().ToArray(), MeshTopology.Triangles, 0);
m_Job.DisposeArrays();
return mesh;
}
public void WaitForCompletion()
{
m_JobHandle.Complete();
}
List<int> TriangleIndicesWithoutHoles()
{
var trianglesWithoutHoles = new List<int>((m_Job.width - 1) * (m_Job.height - 1) * 6);
for (int i = 0; i < m_Job.indices.Length; i += 3)
{
int i1 = m_Job.indices[i];
int i2 = m_Job.indices[i + 1];
int i3 = m_Job.indices[i + 2];
if (i1 != 0 && i2 != 0 && i3 != 0)
{
trianglesWithoutHoles.Add(i1);
trianglesWithoutHoles.Add(i2);
trianglesWithoutHoles.Add(i3);
}
}
if (trianglesWithoutHoles.Count == 0)
{
trianglesWithoutHoles.Add(0);
trianglesWithoutHoles.Add(0);
trianglesWithoutHoles.Add(0);
}
return trianglesWithoutHoles;
}
JobHandle m_JobHandle;
ComputeTerrainMeshJob m_Job;
}
[BurstCompile]
internal struct ComputeTerrainMeshJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<float> heightmap;
[ReadOnly]
public NativeArray<bool> holes;
public int width;
public int height;
public float3 heightmapScale;
public NativeArray<float3> positions;
public NativeArray<float2> uvs;
public NativeArray<float3> normals;
[NativeDisableParallelForRestriction]
public NativeArray<int> indices;
public void DisposeArrays()
{
heightmap.Dispose();
holes.Dispose();
positions.Dispose();
uvs.Dispose();
normals.Dispose();
indices.Dispose();
}
public void Execute(int index)
{
int vertexIndex = index;
int x = vertexIndex % width;
int y = vertexIndex / height;
float3 v = new float3(x, heightmap[y*width +x], y);
positions[vertexIndex] = v * heightmapScale;
uvs[vertexIndex] = v.xz / new float2(width, height);
normals[vertexIndex] = CalculateTerrainNormal(heightmap, x, y, width, height, heightmapScale);
if (x < width - 1 && y < height - 1)
{
int i1 = y * width + x;
int i2 = i1 + 1;
int i3 = i1 + width;
int i4 = i3 + 1;
int faceIndex = x + y * (width - 1);
if (!holes[faceIndex])
{
i1 = i2 = i3 = i4 = 0;
}
indices[6* faceIndex + 0] = i1;
indices[6* faceIndex + 1] = i4;
indices[6* faceIndex + 2] = i2;
indices[6* faceIndex + 3] = i1;
indices[6* faceIndex + 4] = i3;
indices[6* faceIndex + 5] = i4;
}
}
static float3 CalculateTerrainNormal(NativeArray<float> heightmap, int x, int y, int width, int height, float3 scale)
{
float dY, dX;
dX = SampleHeight(x - 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
dX += SampleHeight(x - 1, y, width, height, heightmap, scale.y) * -2.0F;
dX += SampleHeight(x - 1, y + 1, width, height, heightmap, scale.y) * -1.0F;
dX += SampleHeight(x + 1, y - 1, width, height, heightmap, scale.y) * 1.0F;
dX += SampleHeight(x + 1, y, width, height, heightmap, scale.y) * 2.0F;
dX += SampleHeight(x + 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
dX /= scale.x;
dY = SampleHeight(x - 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
dY += SampleHeight(x, y - 1, width, height, heightmap, scale.y) * -2.0F;
dY += SampleHeight(x + 1, y - 1, width, height, heightmap, scale.y) * -1.0F;
dY += SampleHeight(x - 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
dY += SampleHeight(x, y + 1, width, height, heightmap, scale.y) * 2.0F;
dY += SampleHeight(x + 1, y + 1, width, height, heightmap, scale.y) * 1.0F;
dY /= scale.z;
// Cross Product of components of gradient reduces to
return math.normalize(new float3(-dX, 8, -dY));
}
static float SampleHeight(int x, int y, int width, int height, NativeArray<float> heightmap, float scale)
{
x = math.clamp(x, 0, width - 1);
y = math.clamp(y, 0, height - 1);
return heightmap[x + y* width] * scale;
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/TerrainToMesh.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8d38ab270d85f584d9dbdc347f8ba6ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 32f64fed9d745e648a6d1e2ca8c73a03
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

29
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BackendHelpers.cs

@ -0,0 +1,29 @@
using System;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class BackendHelpers
{
internal static string GetFileNameOfShader(RayTracingBackend backend, string fileName)
{
string postFix = backend switch
{
RayTracingBackend.Hardware => "raytrace",
RayTracingBackend.Compute => "compute",
_ => throw new ArgumentOutOfRangeException(nameof(backend), backend, null)
};
return $"{fileName}.{postFix}";
}
internal static Type GetTypeOfShader(RayTracingBackend backend)
{
Type shaderType = backend switch
{
RayTracingBackend.Hardware => typeof(RayTracingShader),
RayTracingBackend.Compute => typeof(ComputeShader),
_ => throw new ArgumentOutOfRangeException(nameof(backend), backend, null)
};
return shaderType;
}
}
}

2
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BackendHelpers.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c72e361e62c22e9438d71d51338ed596

272
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BlockAllocator.cs

@ -0,0 +1,272 @@
using System;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal struct BlockAllocator : IDisposable
{
public struct Block
{
public int offset;
public int count;
public static readonly Block Invalid = new Block() { offset = 0, count = 0 };
}
public struct Allocation
{
public int handle;
public Block block;
public static readonly Allocation Invalid = new Allocation() { handle = -1 };
public readonly bool valid => handle != -1;
}
private int m_FreeElementCount;
private int m_MaxElementCount;
private NativeList<Block> m_freeBlocks;
private NativeList<Block> m_usedBlocks;
private NativeList<int> m_freeSlots;
public int freeElementsCount => m_FreeElementCount;
public int freeBlocks => m_freeBlocks.Length;
public int capacity => m_MaxElementCount;
public int allocatedSize => m_MaxElementCount - m_FreeElementCount;
public void Initialize(int maxElementCounts)
{
m_MaxElementCount = maxElementCounts;
m_FreeElementCount = maxElementCounts;
if (!m_freeBlocks.IsCreated)
m_freeBlocks = new NativeList<Block>(Allocator.Persistent);
else
m_freeBlocks.Clear();
m_freeBlocks.Add(new Block() { offset = 0, count = m_FreeElementCount });
if (!m_usedBlocks.IsCreated)
m_usedBlocks = new NativeList<Block>(Allocator.Persistent);
else
m_usedBlocks.Clear();
if (!m_freeSlots.IsCreated)
m_freeSlots = new NativeList<int>(Allocator.Persistent);
else
m_freeSlots.Clear();
}
private int CalculateGeometricGrowthCapacity(int desiredNewCapacity, int maxAllowedNewCapacity)
{
var oldCapacity = capacity;
if (oldCapacity > maxAllowedNewCapacity - oldCapacity / 2)
{
return maxAllowedNewCapacity; // geometric growth would overflow
}
var geometricNewCapacity = oldCapacity + oldCapacity / 2;
if (geometricNewCapacity < desiredNewCapacity)
return desiredNewCapacity; // geometric growth would be insufficient
else
return geometricNewCapacity;
}
public int Grow(int newDesiredCapacity, int maxAllowedCapacity = Int32.MaxValue)
{
Debug.Assert(newDesiredCapacity > 0, "newDesiredCapacity must be positive");
Debug.Assert(maxAllowedCapacity > 0, "maxAllowedCapacity must be positive");
Debug.Assert(capacity < newDesiredCapacity, "newDesiredCapacity must be greater than curent capacity");
Debug.Assert(maxAllowedCapacity >= newDesiredCapacity, "newDesiredCapacity must be smaller than maxAllowedCapacity");
var newCapacity = CalculateGeometricGrowthCapacity(newDesiredCapacity, maxAllowedCapacity);
var oldCapacity = m_MaxElementCount;
var addedElements = newCapacity - oldCapacity;
Debug.Assert(addedElements > 0);
m_FreeElementCount += addedElements;
m_MaxElementCount = newCapacity;
int blockToMerge = m_freeBlocks.Length;
m_freeBlocks.Add(new Block() { offset = oldCapacity, count = addedElements });
while (blockToMerge != -1)
blockToMerge = MergeBlockFrontBack(blockToMerge);
return m_MaxElementCount;
}
public Allocation GrowAndAllocate(int elementCounts, out int oldCapacity, out int newCapacity)
{
return GrowAndAllocate(elementCounts, Int32.MaxValue, out oldCapacity, out newCapacity);
}
public Allocation GrowAndAllocate(int elementCounts, int maxAllowedCapacity, out int oldCapacity, out int newCapacity)
{
oldCapacity = capacity;
var additionalRequiredElements = m_freeBlocks.IsEmpty ? elementCounts : math.max(elementCounts - m_freeBlocks[m_freeBlocks.Length - 1].count, 0);
if (maxAllowedCapacity < capacity || (maxAllowedCapacity - capacity) < additionalRequiredElements)
{
newCapacity = capacity;
return Allocation.Invalid;
}
newCapacity = additionalRequiredElements > 0 ? Grow(capacity + additionalRequiredElements, maxAllowedCapacity) : capacity;
Debug.Assert(newCapacity >= oldCapacity + additionalRequiredElements);
var alloc = Allocate(elementCounts);
Assert.IsTrue(alloc.valid);
return alloc;
}
public void Dispose()
{
m_MaxElementCount = 0;
m_FreeElementCount = 0;
if (m_freeBlocks.IsCreated)
m_freeBlocks.Dispose();
if (m_usedBlocks.IsCreated)
m_usedBlocks.Dispose();
if (m_freeSlots.IsCreated)
m_freeSlots.Dispose();
}
public Allocation Allocate(int elementCounts)
{
if (elementCounts > m_FreeElementCount || m_freeBlocks.IsEmpty)
return Allocation.Invalid;
int selectedBlock = -1;
int currentBlockCount = 0;
for (int b = 0; b < m_freeBlocks.Length; ++b)
{
Block block = m_freeBlocks[b];
//simple naive allocator, we find the smallest possible space to allocate in our blocks.
if (elementCounts <= block.count && (selectedBlock == -1 || block.count < currentBlockCount))
{
currentBlockCount = block.count;
selectedBlock = b;
}
}
if (selectedBlock == -1)
return Allocation.Invalid;
Block allocationBlock = m_freeBlocks[selectedBlock];
Block split = allocationBlock;
split.offset += elementCounts;
split.count -= elementCounts;
allocationBlock.count = elementCounts;
if (split.count > 0)
m_freeBlocks[selectedBlock] = split;
else
m_freeBlocks.RemoveAtSwapBack(selectedBlock);
int allocationHandle;
if (m_freeSlots.IsEmpty)
{
allocationHandle = m_usedBlocks.Length;
m_usedBlocks.Add(allocationBlock);
}
else
{
allocationHandle = m_freeSlots[m_freeSlots.Length - 1];
m_freeSlots.RemoveAtSwapBack(m_freeSlots.Length - 1);
m_usedBlocks[allocationHandle] = allocationBlock;
}
m_FreeElementCount -= elementCounts;
return new Allocation() { handle = allocationHandle, block = allocationBlock };
}
private int MergeBlockFrontBack(int freeBlockId)
{
Block targetBlock = m_freeBlocks[freeBlockId];
for (int i = 0; i < m_freeBlocks.Length; ++i)
{
if (i == freeBlockId)
continue;
Block freeBlock = m_freeBlocks[i];
bool mergeTargetBlock = false;
if (targetBlock.offset == (freeBlock.offset + freeBlock.count))
{
freeBlock.count += targetBlock.count;
mergeTargetBlock = true;
}
else if (freeBlock.offset == (targetBlock.offset + targetBlock.count))
{
freeBlock.offset = targetBlock.offset;
freeBlock.count += targetBlock.count;
mergeTargetBlock = true;
}
if (mergeTargetBlock)
{
m_freeBlocks[i] = freeBlock;
m_freeBlocks.RemoveAtSwapBack(freeBlockId);
return i == m_freeBlocks.Length ? freeBlockId : i;
}
}
return -1;
}
public void FreeAllocation(in Allocation allocation)
{
Debug.Assert(allocation.valid, "Cannot free invalid allocation");
m_freeSlots.Add(allocation.handle);
m_usedBlocks[allocation.handle] = Block.Invalid;
int blockToMerge = m_freeBlocks.Length;
m_freeBlocks.Add(allocation.block);
while (blockToMerge != -1)
blockToMerge = MergeBlockFrontBack(blockToMerge);
m_FreeElementCount += allocation.block.count;
}
public Allocation[] SplitAllocation(in Allocation allocation, int count)
{
Debug.Assert(allocation.valid, "Invalid allocation");
var newAllocs = new Allocation[count];
var newAllocsSize = allocation.block.count / count;
var newBlock0 = new Block { offset = allocation.block.offset, count = newAllocsSize };
m_usedBlocks[allocation.handle] = newBlock0;
newAllocs[0] = new Allocation() { handle = allocation.handle, block = newBlock0 };
for (int i = 1; i < count; ++i)
{
Block block = new Block { offset = allocation.block.offset + i * newAllocsSize, count = newAllocsSize };
int allocationHandle;
if (m_freeSlots.IsEmpty)
{
allocationHandle = m_usedBlocks.Length;
m_usedBlocks.Add(block);
}
else
{
allocationHandle = m_freeSlots[m_freeSlots.Length - 1];
m_freeSlots.RemoveAtSwapBack(m_freeSlots.Length - 1);
m_usedBlocks[allocationHandle] = block;
}
newAllocs[i] = new Allocation() { handle = allocationHandle, block = block };
}
return newAllocs;
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/BlockAllocator.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 664837c6403e79741a4cb05fc58a66ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

24
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/CopyBuffer.compute

@ -0,0 +1,24 @@
#define GROUP_SIZE 256
#define ELEMENTS_PER_THREAD 8
StructuredBuffer<uint> _SrcBuffer;
int _SrcOffset;
RWStructuredBuffer<uint> _DstBuffer;
int _DstOffset;
int _Size;
#pragma kernel CopyBuffer
[numthreads(GROUP_SIZE,1,1)]
void CopyBuffer(uint gidx : SV_DispatchThreadID)
{
for (int i = 0; i < ELEMENTS_PER_THREAD; ++i)
{
int elemIndex = gidx * ELEMENTS_PER_THREAD + i;
if (elemIndex >= _Size)
return;
uint value = _SrcBuffer[elemIndex + _SrcOffset];
_DstBuffer[elemIndex + _DstOffset] = value;
}
}

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/CopyBuffer.compute.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 033e2ad50853a92498e5ca004bf0a68c
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

68
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/GraphicsHelpers.cs

@ -0,0 +1,68 @@
using Unity.Mathematics;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class GraphicsHelpers
{
static public void CopyBuffer(ComputeShader copyShader, CommandBuffer cmd, GraphicsBuffer src, int srcOffsetInDWords, GraphicsBuffer dst, int dstOffsetInDwords, int sizeInDWords)
{
const int groupSize = 256;
const int elementsPerThread = 8;
const int maxThreadGroups = 65535; // gfx device limitation
const int maxBatchSizeInDWords = groupSize * elementsPerThread * maxThreadGroups;
int remainingDWords = sizeInDWords;
cmd.SetComputeBufferParam(copyShader, 0, "_SrcBuffer", src);
cmd.SetComputeBufferParam(copyShader, 0, "_DstBuffer", dst);
while (remainingDWords > 0)
{
int batchSize = math.min(remainingDWords, maxBatchSizeInDWords);
cmd.SetComputeIntParam(copyShader, "_SrcOffset", srcOffsetInDWords);
cmd.SetComputeIntParam(copyShader, "_DstOffset", dstOffsetInDwords);
cmd.SetComputeIntParam(copyShader, "_Size", batchSize);
cmd.DispatchCompute(copyShader, 0, DivUp(batchSize, elementsPerThread * groupSize), 1, 1);
remainingDWords -= batchSize;
srcOffsetInDWords += batchSize;
dstOffsetInDwords += batchSize;
}
}
static public void CopyBuffer(ComputeShader copyShader, GraphicsBuffer src, int srcOffsetInDWords, GraphicsBuffer dst, int dstOffsetInDwords, int sizeInDwords)
{
CommandBuffer cmd = new CommandBuffer();
CopyBuffer(copyShader, cmd, src, srcOffsetInDWords, dst, dstOffsetInDwords, sizeInDwords);
Graphics.ExecuteCommandBuffer(cmd);
}
static public void ReallocateBuffer(ComputeShader copyShader, int oldCapacity, int newCapacity, int elementSizeInBytes, ref GraphicsBuffer buffer)
{
int bufferStrideInBytes = buffer.stride;
var newBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, newCapacity * elementSizeInBytes / bufferStrideInBytes, bufferStrideInBytes);
CopyBuffer(copyShader, buffer, 0, newBuffer, 0, oldCapacity * elementSizeInBytes / 4);
buffer.Dispose();
buffer = newBuffer;
}
public const int MaxGraphicsBufferSizeInBytes = int.MaxValue;
static public int DivUp(int x, int y) => (x + y - 1) / y;
static public int DivUp(int x, uint y) => (x + (int)y - 1) / (int)y;
static public uint DivUp(uint x, uint y) => (x + y - 1) / y;
static public uint3 DivUp(uint3 x, uint3 y) => (x + y - 1) / y;
/// <summary>
/// Immediately executes the pending work on the command buffer.
/// This is useful for preventing TDR, which can happen when scheduling too much work in one CommandBuffer.
/// </summary>
/// <param name="cmd">Command buffer to execute.</param>
static public void Flush(CommandBuffer cmd)
{
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
GL.Flush();
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/GraphicsHelpers.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c86b8c7f5cb48934392704c99b69c9b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

163
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/PersistentGPUArray.cs

@ -0,0 +1,163 @@
using System;
using System.Collections;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal sealed class PersistentGpuArray<Tstruct> : IDisposable
where Tstruct : struct
{
BlockAllocator m_SlotAllocator;
ComputeBuffer m_GpuBuffer;
NativeArray<Tstruct> m_CpuList;
BitArray m_Updates;
bool m_gpuBufferDirty = true;
int m_ElementCount = 0;
public int elementCount { get { return m_ElementCount; } }
public PersistentGpuArray(int initialSize)
{
m_SlotAllocator.Initialize(initialSize);
m_GpuBuffer = new ComputeBuffer(initialSize, Marshal.SizeOf<Tstruct>());
m_CpuList = new NativeArray<Tstruct>(initialSize, Allocator.Persistent);
m_Updates = new BitArray(initialSize);
m_ElementCount = 0;
}
public void Dispose()
{
m_ElementCount = 0;
m_SlotAllocator.Dispose();
m_GpuBuffer.Dispose();
m_CpuList.Dispose();
}
public BlockAllocator.Allocation Add(Tstruct element)
{
m_ElementCount++;
var slotAllocation = m_SlotAllocator.Allocate(1);
if (!slotAllocation.valid)
{
Grow();
slotAllocation = m_SlotAllocator.Allocate(1);
Assert.IsTrue(slotAllocation.valid);
}
m_CpuList[slotAllocation.block.offset] = element;
m_Updates[slotAllocation.block.offset] = true;
m_gpuBufferDirty = true;
return slotAllocation;
}
public BlockAllocator.Allocation[] Add(int elementCount)
{
m_ElementCount+= elementCount;
var slotAllocation = m_SlotAllocator.Allocate(elementCount);
if (!slotAllocation.valid)
{
Grow();
slotAllocation = m_SlotAllocator.Allocate(elementCount);
Assert.IsTrue(slotAllocation.valid);
}
return m_SlotAllocator.SplitAllocation(slotAllocation, elementCount);
}
public void Remove(BlockAllocator.Allocation allocation)
{
m_ElementCount--;
m_SlotAllocator.FreeAllocation(allocation);
}
public void Clear()
{
m_ElementCount = 0;
var currentCapacity = m_SlotAllocator.capacity;
m_SlotAllocator.Dispose();
m_SlotAllocator = new BlockAllocator();
m_SlotAllocator.Initialize(currentCapacity);
m_Updates = new BitArray(currentCapacity);
m_gpuBufferDirty = false;
}
public void Set(BlockAllocator.Allocation allocation, Tstruct element)
{
m_CpuList[allocation.block.offset] = element;
m_Updates[allocation.block.offset] = true;
m_gpuBufferDirty = true;
}
public Tstruct Get(BlockAllocator.Allocation allocation)
{
return m_CpuList[allocation.block.offset];
}
public void ModifyForEach(Func<Tstruct, Tstruct> lambda)
{
for (int i = 0; i < m_CpuList.Length; ++i)
{
m_CpuList[i] = lambda(m_CpuList[i]);
m_Updates[i] = true;
}
m_gpuBufferDirty = true;
}
// Note: this should ideally be used with only one command buffer. If used with more than one cmd buffers, the order of their execution is important.
public ComputeBuffer GetGpuBuffer(CommandBuffer cmd)
{
if (m_gpuBufferDirty)
{
int copyStartIndex = -1;
for (int i = 0; i < m_Updates.Length; ++i)
{
if (m_Updates[i])
{
if (copyStartIndex == -1)
copyStartIndex = i;
m_Updates[i] = false;
}
else if (copyStartIndex != -1)
{
int copyEndIndex = i;
cmd.SetBufferData(m_GpuBuffer, m_CpuList, copyStartIndex, copyStartIndex, copyEndIndex - copyStartIndex);
copyStartIndex = -1;
}
}
if (copyStartIndex != -1)
{
int copyEndIndex = m_Updates.Length;
cmd.SetBufferData(m_GpuBuffer, m_CpuList, copyStartIndex, copyStartIndex, copyEndIndex - copyStartIndex);
}
m_gpuBufferDirty = false;
}
return m_GpuBuffer;
}
private void Grow()
{
var oldCapacity = m_SlotAllocator.capacity;
m_SlotAllocator.Grow(m_SlotAllocator.capacity + 1);
m_GpuBuffer.Dispose();
m_GpuBuffer = new ComputeBuffer(m_SlotAllocator.capacity, Marshal.SizeOf<Tstruct>());
var oldList = m_CpuList;
m_CpuList = new NativeArray<Tstruct>(m_SlotAllocator.capacity, Allocator.Persistent);
NativeArray<Tstruct>.Copy(oldList, m_CpuList, oldCapacity);
oldList.Dispose();
var oldUpdates = m_Updates;
m_Updates = new BitArray(m_SlotAllocator.capacity);
for (int i = 0; i < oldCapacity; ++i)
m_Updates[i] = oldUpdates[i];
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/PersistentGPUArray.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b3fae1cd111b8f4895b008c99a99803
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

22
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/Utils.cs

@ -0,0 +1,22 @@
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal static class Utils
{
public static void Destroy(UnityEngine.Object obj)
{
if (obj != null)
{
#if UNITY_EDITOR
if (Application.isPlaying && !UnityEditor.EditorApplication.isPaused)
UnityEngine.Object.Destroy(obj);
else
UnityEngine.Object.DestroyImmediate(obj);
#else
UnityEngine.Object.Destroy(obj);
#endif
}
}
}
}

2
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Common/Utilities/Utils.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 38870b584486a484b83bf1b5e0e302a9

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f242eaea8e7ce454ea303340a67e3743
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

95
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/BLASPositionsPool.cs

@ -0,0 +1,95 @@
using System;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine.Assertions;
using UnityEngine.Rendering.RadeonRays;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal struct VertexBufferChunk
{
public GraphicsBuffer vertices;
public int verticesStartOffset; // in DWORD
public uint vertexCount;
public uint vertexStride; // in DWORD
public int baseVertex;
}
internal sealed class BLASPositionsPool : IDisposable
{
public BLASPositionsPool(ComputeShader copyPositionsShader, ComputeShader copyShader)
{
m_VerticesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, intialVertexCount * VertexSizeInDwords, 4);
m_VerticesAllocator = new BlockAllocator();
m_VerticesAllocator.Initialize(intialVertexCount);
m_CopyPositionsShader = copyPositionsShader;
m_CopyVerticesKernel = m_CopyPositionsShader.FindKernel("CopyVertexBuffer");
m_CopyShader = copyShader;
}
public void Dispose()
{
m_VerticesBuffer.Dispose();
m_VerticesAllocator.Dispose();
}
public const int VertexSizeInDwords = 3;
public GraphicsBuffer VertexBuffer { get { return m_VerticesBuffer; } }
public void Clear()
{
m_VerticesBuffer.Dispose();
m_VerticesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, intialVertexCount * VertexSizeInDwords, 4);
m_VerticesAllocator.Dispose();
m_VerticesAllocator = new BlockAllocator();
m_VerticesAllocator.Initialize(intialVertexCount);
}
const int intialVertexCount = 1000;
GraphicsBuffer m_VerticesBuffer;
BlockAllocator m_VerticesAllocator;
readonly ComputeShader m_CopyPositionsShader;
readonly int m_CopyVerticesKernel;
readonly ComputeShader m_CopyShader;
const uint kItemsPerWorkgroup = 48u * 128u;
public bool Add(VertexBufferChunk info, out BlockAllocator.Allocation verticesAllocation)
{
verticesAllocation = m_VerticesAllocator.Allocate((int)info.vertexCount*3);
if (!verticesAllocation.valid)
{
verticesAllocation = m_VerticesAllocator.GrowAndAllocate((int)info.vertexCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / UnsafeUtility.SizeOf<float3>(), out int oldCapacity, out int newCapacity);
if (!verticesAllocation.valid)
return false;
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, UnsafeUtility.SizeOf<float3>(), ref m_VerticesBuffer);
}
var cmd = new CommandBuffer();
cmd.SetComputeIntParam(m_CopyPositionsShader, "_InputPosBufferCount", (int)info.vertexCount);
cmd.SetComputeIntParam(m_CopyPositionsShader, "_InputPosBufferOffset", info.verticesStartOffset);
cmd.SetComputeIntParam(m_CopyPositionsShader, "_InputBaseVertex", info.baseVertex);
cmd.SetComputeIntParam(m_CopyPositionsShader, "_InputPosBufferStride", (int)info.vertexStride);
cmd.SetComputeIntParam(m_CopyPositionsShader, "_OutputPosBufferOffset", verticesAllocation.block.offset * VertexSizeInDwords);
cmd.SetComputeBufferParam(m_CopyPositionsShader, m_CopyVerticesKernel, "_InputPosBuffer", info.vertices);
cmd.SetComputeBufferParam(m_CopyPositionsShader, m_CopyVerticesKernel, "_OutputPosBuffer", m_VerticesBuffer);
cmd.DispatchCompute(m_CopyPositionsShader, m_CopyVerticesKernel, (int)Common.CeilDivide(info.vertexCount, kItemsPerWorkgroup), 1, 1);
Graphics.ExecuteCommandBuffer(cmd);
return true;
}
public void Remove(ref BlockAllocator.Allocation verticesAllocation)
{
m_VerticesAllocator.FreeAllocation(verticesAllocation);
verticesAllocation = BlockAllocator.Allocation.Invalid;
}
}
}

2
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/BLASPositionsPool.cs.meta

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b53cc1c0c355a50498ab7a4832b90582

695
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingAccelStruct.cs

@ -0,0 +1,695 @@
using System;
using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine.Assertions;
using UnityEngine.Rendering.RadeonRays;
#if UNITY_EDITOR
using UnityEditor.Embree;
#endif
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal class ComputeRayTracingAccelStruct : IRayTracingAccelStruct
{
internal ComputeRayTracingAccelStruct(
AccelerationStructureOptions options, RayTracingResources resources,
ReferenceCounter counter, int blasBufferInitialSizeBytes = 64 * 1024 * 1024)
{
m_CopyShader = resources.copyBuffer;
RadeonRaysShaders shaders = new RadeonRaysShaders();
shaders.bitHistogram = resources.bitHistogram;
shaders.blockReducePart = resources.blockReducePart;
shaders.blockScan = resources.blockScan;
shaders.buildHlbvh = resources.buildHlbvh;
shaders.restructureBvh = resources.restructureBvh;
shaders.scatter = resources.scatter;
m_RadeonRaysAPI = new RadeonRaysAPI(shaders);
m_BuildFlags = options.buildFlags;
#if UNITY_EDITOR
m_UseCpuBuild = options.useCPUBuild;
#endif
m_Blases = new Dictionary<(int mesh, int subMeshIndex), MeshBlas>();
var blasNodeCount = blasBufferInitialSizeBytes / RadeonRaysAPI.BvhInternalNodeSizeInBytes();
m_BlasBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, blasNodeCount, RadeonRaysAPI.BvhInternalNodeSizeInBytes());
m_BlasLeavesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, blasNodeCount, RadeonRaysAPI.BvhLeafNodeSizeInBytes());
m_BlasPositions = new BLASPositionsPool(resources.copyPositions, resources.copyBuffer);
m_BlasAllocator = new BlockAllocator();
m_BlasAllocator.Initialize(blasNodeCount);
m_BlasLeavesAllocator = new BlockAllocator();
m_BlasLeavesAllocator.Initialize(blasNodeCount);
m_Counter = counter;
m_Counter.Inc();
}
internal GraphicsBuffer topLevelBvhBuffer { get { return m_TopLevelAccelStruct?.topLevelBvh; } }
internal GraphicsBuffer bottomLevelBvhBuffer { get { return m_TopLevelAccelStruct?.bottomLevelBvhs; } }
internal GraphicsBuffer instanceInfoBuffer { get { return m_TopLevelAccelStruct?.instanceInfos; } }
public void Dispose()
{
foreach (var blas in m_Blases.Values)
{
if (blas.buildInfo.triangleIndices != null)
blas.buildInfo.triangleIndices.Dispose();
}
m_Counter.Dec();
m_RadeonRaysAPI.Dispose();
m_BlasBuffer.Dispose();
m_BlasLeavesBuffer.Dispose();
m_BlasPositions.Dispose();
m_BlasAllocator.Dispose();
m_BlasLeavesAllocator.Dispose();
m_TopLevelAccelStruct?.Dispose();
}
public int AddInstance(MeshInstanceDesc meshInstance)
{
var blas = GetOrAllocateMeshBlas(meshInstance.mesh, meshInstance.subMeshIndex);
blas.IncRef();
FreeTopLevelAccelStruct();
int handle = NewHandle();
m_RadeonInstances.Add(handle, new RadeonRaysInstance
{
geomKey = (meshInstance.mesh.GetHashCode(), meshInstance.subMeshIndex),
blas = blas,
instanceMask = meshInstance.mask,
triangleCullingEnabled = meshInstance.enableTriangleCulling,
invertTriangleCulling = meshInstance.frontTriangleCounterClockwise,
userInstanceID = meshInstance.instanceID == 0xFFFFFFFF ? (uint)handle : meshInstance.instanceID,
localToWorldTransform = ConvertTranform(meshInstance.localToWorldMatrix)
});
return handle;
}
public void RemoveInstance(int instanceHandle)
{
ReleaseHandle(instanceHandle);
m_RadeonInstances.Remove(instanceHandle, out RadeonRaysInstance entry);
var meshBlas = entry.blas;
meshBlas.DecRef();
if (meshBlas.IsUnreferenced())
DeleteMeshBlas(entry.geomKey, meshBlas);
FreeTopLevelAccelStruct();
}
public void ClearInstances()
{
m_FreeHandles.Clear();
m_RadeonInstances.Clear();
foreach (var blas in m_Blases.Values)
{
if (blas.buildInfo.triangleIndices != null)
blas.buildInfo.triangleIndices.Dispose();
}
m_Blases.Clear();
m_BlasPositions.Clear();
var currentCapacity = m_BlasAllocator.capacity;
m_BlasAllocator.Dispose();
m_BlasAllocator = new BlockAllocator();
m_BlasAllocator.Initialize(currentCapacity);
currentCapacity = m_BlasLeavesAllocator.capacity;
m_BlasLeavesAllocator.Dispose();
m_BlasLeavesAllocator = new BlockAllocator();
m_BlasLeavesAllocator.Initialize(currentCapacity);
FreeTopLevelAccelStruct();
}
public void UpdateInstanceTransform(int instanceHandle, Matrix4x4 localToWorldMatrix)
{
m_RadeonInstances[instanceHandle].localToWorldTransform = ConvertTranform(localToWorldMatrix);
FreeTopLevelAccelStruct();
}
public void UpdateInstanceID(int instanceHandle, uint instanceID)
{
m_RadeonInstances[instanceHandle].userInstanceID = instanceID;
FreeTopLevelAccelStruct();
}
public void UpdateInstanceMask(int instanceHandle, uint mask)
{
m_RadeonInstances[instanceHandle].instanceMask = mask;
FreeTopLevelAccelStruct();
}
public void Build(CommandBuffer cmd, GraphicsBuffer scratchBuffer)
{
var requiredScratchSize = GetBuildScratchBufferRequiredSizeInBytes();
if (requiredScratchSize > 0 && (scratchBuffer == null || ((ulong)(scratchBuffer.count * scratchBuffer.stride) < requiredScratchSize)))
{
throw new System.ArgumentException("scratchBuffer size is too small");
}
if (requiredScratchSize > 0 && scratchBuffer.stride != 4)
{
throw new System.ArgumentException("scratchBuffer stride must be 4");
}
if (m_TopLevelAccelStruct != null)
return;
CreateBvh(cmd, scratchBuffer);
}
public ulong GetBuildScratchBufferRequiredSizeInBytes()
{
return GetBvhBuildScratchBufferSizeInDwords() * 4;
}
private void FreeTopLevelAccelStruct()
{
m_TopLevelAccelStruct?.Dispose();
m_TopLevelAccelStruct = null;
}
private MeshBlas GetOrAllocateMeshBlas(Mesh mesh, int subMeshIndex)
{
MeshBlas blas;
if (m_Blases.TryGetValue((mesh.GetHashCode(), subMeshIndex), out blas))
return blas;
blas = new MeshBlas();
AllocateBlas(mesh, subMeshIndex, blas);
m_Blases[(mesh.GetHashCode(), subMeshIndex)] = blas;
return blas;
}
void AllocateBlas(Mesh mesh, int submeshIndex, MeshBlas blas)
{
var bvhNodeSizeInDwords = RadeonRaysAPI.BvhInternalNodeSizeInDwords();
mesh.indexBufferTarget |= GraphicsBuffer.Target.Raw;
mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
SubMeshDescriptor submeshDescriptor = mesh.GetSubMesh(submeshIndex);
using var vertexBuffer = LoadPositionBuffer(mesh, out int stride, out int offset);
GraphicsBuffer indexBuffer = null;
#if UNITY_EDITOR
if (!m_UseCpuBuild)
#endif
indexBuffer = LoadIndexBuffer(mesh);
var vertexBufferDesc = new VertexBufferChunk();
vertexBufferDesc.vertices = vertexBuffer;
vertexBufferDesc.verticesStartOffset = offset;
vertexBufferDesc.baseVertex = submeshDescriptor.baseVertex + submeshDescriptor.firstVertex;
vertexBufferDesc.vertexCount = (uint)submeshDescriptor.vertexCount;
vertexBufferDesc.vertexStride = (uint)stride;
if (!m_BlasPositions.Add(vertexBufferDesc, out blas.blasVertices))
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
var meshBuildInfo = new MeshBuildInfo();
meshBuildInfo.vertices = m_BlasPositions.VertexBuffer;
meshBuildInfo.verticesStartOffset = blas.blasVertices.block.offset * BLASPositionsPool.VertexSizeInDwords;
meshBuildInfo.baseVertex = 0;
meshBuildInfo.triangleIndices = indexBuffer;
meshBuildInfo.vertexCount = (uint)blas.blasVertices.block.count;
meshBuildInfo.triangleCount = (uint)submeshDescriptor.indexCount / 3;
meshBuildInfo.indicesStartOffset = submeshDescriptor.indexStart;
meshBuildInfo.baseIndex = -submeshDescriptor.firstVertex;
meshBuildInfo.indexFormat = mesh.indexFormat == IndexFormat.UInt32 ? RadeonRays.IndexFormat.Int32 : RadeonRays.IndexFormat.Int16;
meshBuildInfo.vertexStride = 3;
blas.buildInfo = meshBuildInfo;
#if UNITY_EDITOR
if (m_UseCpuBuild)
{
blas.indicesForCpuBuild = new List<int>();
mesh.GetTriangles(blas.indicesForCpuBuild, submeshIndex, false);
blas.baseIndexForCpuBuild = -submeshDescriptor.firstVertex;
blas.verticesForCpuBuild = new List<Vector3>();
mesh.GetVertices(blas.verticesForCpuBuild);
blas.bvhAlloc = BlockAllocator.Allocation.Invalid;
}
else
#endif
{
var requirements = m_RadeonRaysAPI.GetMeshBuildMemoryRequirements(meshBuildInfo, ConvertFlagsToGpuBuild(m_BuildFlags));
var allocationNodeCount = (ulong)(requirements.bvhSizeInDwords / (ulong)bvhNodeSizeInDwords);
if (allocationNodeCount > int.MaxValue)
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
blas.bvhAlloc = AllocateBlasInternalNodes((int)allocationNodeCount);
if (!blas.bvhAlloc.valid)
{
m_BlasPositions.Remove(ref blas.blasVertices);
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
}
blas.bvhLeavesAlloc = AllocateBlasLeafNodes((int)meshBuildInfo.triangleCount);
if (!blas.bvhLeavesAlloc.valid)
{
m_BlasPositions.Remove(ref blas.blasVertices);
m_BlasAllocator.FreeAllocation(blas.bvhAlloc);
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
}
}
}
private GraphicsBuffer LoadIndexBuffer(Mesh mesh)
{
Debug.Assert((mesh.indexBufferTarget & GraphicsBuffer.Target.Raw) != 0 || (mesh.GetIndices(0) != null && mesh.GetIndices(0).Length != 0),
"Cant use a mesh buffer that is not raw and has no CPU index information.");
return mesh.GetIndexBuffer();
}
GraphicsBuffer LoadPositionBuffer(Mesh mesh, out int stride, out int offset)
{
VertexAttribute attribute = VertexAttribute.Position;
Debug.Assert(mesh.HasVertexAttribute(attribute), "Cant use a mesh buffer that has no positions.");
int stream = mesh.GetVertexAttributeStream(attribute);
stride = mesh.GetVertexBufferStride(stream) / 4;
offset = mesh.GetVertexAttributeOffset(attribute) / 4;
return mesh.GetVertexBuffer(stream);
}
private void DeleteMeshBlas((int mesh, int subMeshIndex) geomKey, MeshBlas blas)
{
m_BlasAllocator.FreeAllocation(blas.bvhAlloc);
blas.bvhAlloc = BlockAllocator.Allocation.Invalid;
m_BlasLeavesAllocator.FreeAllocation(blas.bvhLeavesAlloc);
blas.bvhLeavesAlloc = BlockAllocator.Allocation.Invalid;
m_BlasPositions.Remove(ref blas.blasVertices);
if (blas.buildInfo.triangleIndices != null)
blas.buildInfo.triangleIndices.Dispose();
m_Blases.Remove(geomKey);
}
private ulong GetBvhBuildScratchBufferSizeInDwords()
{
#if UNITY_EDITOR
if (m_UseCpuBuild)
return 0;
#endif
var bvhNodeSizeInDwords = RadeonRaysAPI.BvhInternalNodeSizeInDwords();
ulong scratchBufferSize = 0;
foreach (var meshBlas in m_Blases)
{
if (meshBlas.Value.bvhBuilt)
continue;
var requirements = m_RadeonRaysAPI.GetMeshBuildMemoryRequirements(meshBlas.Value.buildInfo, ConvertFlagsToGpuBuild(m_BuildFlags));
Assert.AreEqual(requirements.bvhSizeInDwords / (ulong)bvhNodeSizeInDwords, (ulong)meshBlas.Value.bvhAlloc.block.count);
scratchBufferSize = math.max(scratchBufferSize, requirements.buildScratchSizeInDwords);
}
var topLevelScratchSize = m_RadeonRaysAPI.GetSceneBuildMemoryRequirements((uint)m_RadeonInstances.Count).buildScratchSizeInDwords;
scratchBufferSize = math.max(scratchBufferSize, topLevelScratchSize);
scratchBufferSize = math.max(4, scratchBufferSize);
return scratchBufferSize;
}
private void CreateBvh(CommandBuffer cmd, GraphicsBuffer scratchBuffer)
{
BuildMissingBottomLevelAccelStructs(cmd, scratchBuffer);
BuildTopLevelAccelStruct(cmd, scratchBuffer);
}
private void BuildMissingBottomLevelAccelStructs(CommandBuffer cmd, GraphicsBuffer scratchBuffer)
{
foreach (var meshBlas in m_Blases.Values)
{
if (meshBlas.bvhBuilt)
continue;
meshBlas.buildInfo.vertices = m_BlasPositions.VertexBuffer;
#if UNITY_EDITOR
if (m_UseCpuBuild)
{
CpuBuildForBottomLevelAccelStruct(cmd, meshBlas);
}
else
#endif
{
var blasDesc = new BottomLevelLevelAccelStruct(){
bvh = m_BlasBuffer,
bvhOffset = (uint)meshBlas.bvhAlloc.block.offset,
bvhLeaves = m_BlasLeavesBuffer,
bvhLeavesOffset = (uint)meshBlas.bvhLeavesAlloc.block.offset,
};
m_RadeonRaysAPI.BuildMeshAccelStruct(
cmd,
meshBlas.buildInfo, ConvertFlagsToGpuBuild(m_BuildFlags),
scratchBuffer, in blasDesc);
meshBlas.buildInfo.triangleIndices.Dispose();
meshBlas.buildInfo.triangleIndices = null;
}
meshBlas.bvhBuilt = true;
}
}
private void BuildTopLevelAccelStruct(CommandBuffer cmd, GraphicsBuffer scratchBuffer)
{
var radeonRaysInstances = new RadeonRays.Instance[m_RadeonInstances.Count];
int i = 0;
foreach (var instance in m_RadeonInstances.Values)
{
radeonRaysInstances[i].meshAccelStructOffset = (uint)instance.blas.bvhAlloc.block.offset;
radeonRaysInstances[i].localToWorldTransform = instance.localToWorldTransform;
radeonRaysInstances[i].instanceMask = instance.instanceMask;
radeonRaysInstances[i].vertexOffset = (uint)instance.blas.blasVertices.block.offset * BLASPositionsPool.VertexSizeInDwords;
radeonRaysInstances[i].meshAccelStructLeavesOffset = (uint)instance.blas.bvhLeavesAlloc.block.offset;
radeonRaysInstances[i].triangleCullingEnabled = instance.triangleCullingEnabled;
radeonRaysInstances[i].invertTriangleCulling = instance.invertTriangleCulling;
radeonRaysInstances[i].userInstanceID = instance.userInstanceID;
i++;
}
m_TopLevelAccelStruct?.Dispose();
#if UNITY_EDITOR
if (m_UseCpuBuild)
m_TopLevelAccelStruct = CpuBuildForTopLevelAccelStruct(cmd, radeonRaysInstances);
else
#endif
m_TopLevelAccelStruct = m_RadeonRaysAPI.BuildSceneAccelStruct(cmd, m_BlasBuffer, radeonRaysInstances, scratchBuffer);
}
#if UNITY_EDITOR
void CpuBuildForBottomLevelAccelStruct(CommandBuffer cmd, MeshBlas blas)
{
var vertices = blas.verticesForCpuBuild;
var indices = blas.indicesForCpuBuild;
var prims = new GpuBvhPrimitiveDescriptor[blas.buildInfo.triangleCount];
for (int i = 0; i < blas.buildInfo.triangleCount; ++i)
{
var triangleIndices = GetFaceIndices(indices, i);
var triangle = GetTriangle(vertices, triangleIndices);
AABB aabb = new AABB();
aabb.Encapsulate(triangle.v0);
aabb.Encapsulate(triangle.v1);
aabb.Encapsulate(triangle.v2);
prims[i].primID = (uint)i;
prims[i].lowerBound = aabb.Min;
prims[i].upperBound = aabb.Max;
}
blas.indicesForCpuBuild = null;
blas.verticesForCpuBuild = null;
var options = ConvertFlagsToCpuBuild(m_BuildFlags, false);
var bvhBlob = GpuBvh.Build(options, prims);
var internalNodeCount = bvhBlob[0];
var leafNodeCount = bvhBlob[1];
var bvhSizeInDwords = RadeonRaysAPI.BvhInternalNodeSizeInDwords() * ((int)internalNodeCount + 1);
var bvhLeavesSizeInDwords = bvhBlob.Length - bvhSizeInDwords;
blas.bvhAlloc = AllocateBlasInternalNodes((int)internalNodeCount);
if (!blas.bvhAlloc.valid)
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
blas.bvhLeavesAlloc = AllocateBlasLeafNodes((int)leafNodeCount);
if (!blas.bvhAlloc.valid)
{
m_BlasAllocator.FreeAllocation(blas.bvhAlloc);
throw new UnifiedRayTracingException("Can't allocate a GraphicsBuffer bigger than 2GB", UnifiedRayTracingError.OutOfGraphicsBufferMemory);
}
// Fill triangle indices in leaf nodes.
int leafOffset = bvhSizeInDwords;
for (int i = 0; i < leafNodeCount; ++i)
{
var triangleIndices = GetFaceIndices(indices,(int) bvhBlob[leafOffset+3]);
bvhBlob[leafOffset] = (uint)(triangleIndices.x + blas.baseIndexForCpuBuild);
bvhBlob[leafOffset+1] = (uint)(triangleIndices.y + blas.baseIndexForCpuBuild);
bvhBlob[leafOffset+2] = (uint)(triangleIndices.z + blas.baseIndexForCpuBuild);
leafOffset += 4;
}
var bvhStartInDwords = blas.bvhAlloc.block.offset * RadeonRaysAPI.BvhInternalNodeSizeInDwords();
cmd.SetBufferData(m_BlasBuffer, bvhBlob, 0, bvhStartInDwords, bvhSizeInDwords);
var bvhLeavesStartInDwords = blas.bvhLeavesAlloc.block.offset * RadeonRaysAPI.BvhLeafNodeSizeInDwords();
cmd.SetBufferData(m_BlasLeavesBuffer, bvhBlob, bvhSizeInDwords, bvhLeavesStartInDwords, bvhLeavesSizeInDwords);
// read mesh aabb from bvh header.
blas.aabbForCpuBuild = new AABB();
blas.aabbForCpuBuild.Min.x = math.asfloat(bvhBlob[4]);
blas.aabbForCpuBuild.Min.y = math.asfloat(bvhBlob[5]);
blas.aabbForCpuBuild.Min.z = math.asfloat(bvhBlob[6]);
blas.aabbForCpuBuild.Max.x = math.asfloat(bvhBlob[7]);
blas.aabbForCpuBuild.Max.y = math.asfloat(bvhBlob[8]);
blas.aabbForCpuBuild.Max.z = math.asfloat(bvhBlob[9]);
}
TopLevelAccelStruct CpuBuildForTopLevelAccelStruct(CommandBuffer cmd, RadeonRays.Instance[] radeonRaysInstances)
{
var prims = new GpuBvhPrimitiveDescriptor[m_RadeonInstances.Count];
int i = 0;
foreach (var instance in m_RadeonInstances.Values)
{
var blas = instance.blas;
AABB aabb = blas.aabbForCpuBuild;
var m = ConvertTranform(instance.localToWorldTransform);
var bounds = GeometryUtility.CalculateBounds(new Vector3[]
{ new Vector3(aabb.Min.x, aabb.Min.y, aabb.Max.z),
new Vector3(aabb.Min.x, aabb.Max.y, aabb.Min.z),
new Vector3(aabb.Min.x, aabb.Max.y, aabb.Max.z),
new Vector3(aabb.Max.x, aabb.Min.y, aabb.Max.z),
new Vector3(aabb.Max.x, aabb.Max.y, aabb.Min.z),
new Vector3(aabb.Max.x, aabb.Max.y, aabb.Max.z)
}, m);
prims[i].primID = (uint)i;
prims[i].lowerBound = bounds.min;
prims[i].upperBound = bounds.max;
i++;
}
if (m_RadeonInstances.Count != 0)
{
var options = ConvertFlagsToCpuBuild(m_BuildFlags, true);
var bvhBlob = GpuBvh.Build(options, prims);
var bvhSizeInDwords = bvhBlob.Length;
var result = m_RadeonRaysAPI.CreateSceneAccelStructBuffers(m_BlasBuffer, (uint)bvhSizeInDwords, radeonRaysInstances);
cmd.SetBufferData(result.topLevelBvh, bvhBlob);
return result;
}
else
{
return m_RadeonRaysAPI.CreateSceneAccelStructBuffers(m_BlasBuffer, 0, radeonRaysInstances);
}
}
GpuBvhBuildOptions ConvertFlagsToCpuBuild(BuildFlags flags, bool isTopLevel)
{
GpuBvhBuildQuality quality = GpuBvhBuildQuality.Medium;
if ((flags & BuildFlags.PreferFastBuild) != 0 && (flags & BuildFlags.PreferFastTrace) == 0)
quality = GpuBvhBuildQuality.Low;
else if ((flags & BuildFlags.PreferFastTrace) != 0 && (flags & BuildFlags.PreferFastBuild) == 0)
quality = GpuBvhBuildQuality.High;
return new GpuBvhBuildOptions
{
quality = quality,
minLeafSize = (flags & BuildFlags.MinimizeMemory) != 0 && !isTopLevel ? 4u : 1u,
maxLeafSize = isTopLevel ? 1u : 4u,
allowPrimitiveSplits = !isTopLevel && (flags & BuildFlags.MinimizeMemory) == 0,
isTopLevel = isTopLevel
};
}
#endif
RadeonRays.BuildFlags ConvertFlagsToGpuBuild(BuildFlags flags)
{
if ((flags & BuildFlags.PreferFastBuild) != 0 && (flags & BuildFlags.PreferFastTrace) == 0)
return RadeonRays.BuildFlags.PreferFastBuild;
else
return RadeonRays.BuildFlags.None;
}
public void Bind(CommandBuffer cmd, string name, IRayTracingShader shader)
{
shader.SetBufferParam(cmd, Shader.PropertyToID(name + "bvh"), topLevelBvhBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID(name + "bottomBvhs"), bottomLevelBvhBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID(name + "bottomBvhLeaves"), m_BlasLeavesBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID(name + "instanceInfos"), instanceInfoBuffer);
shader.SetBufferParam(cmd, Shader.PropertyToID(name + "vertexBuffer"), m_BlasPositions.VertexBuffer);
shader.SetIntParam(cmd, Shader.PropertyToID(name + "vertexStride"), 3);
}
static private RadeonRays.Transform ConvertTranform(Matrix4x4 input)
{
return new RadeonRays.Transform()
{
row0 = input.GetRow(0),
row1 = input.GetRow(1),
row2 = input.GetRow(2)
};
}
static private Matrix4x4 ConvertTranform(RadeonRays.Transform input)
{
var m = new Matrix4x4();
m.SetRow(0, input.row0);
m.SetRow(1, input.row1);
m.SetRow(2, input.row2);
m.SetRow(3, new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
return m;
}
static int3 GetFaceIndices(List<int> indices, int triangleIdx)
{
return new int3(
indices[3 * triangleIdx],
indices[3 * triangleIdx + 1],
indices[3 * triangleIdx + 2]);
}
struct Triangle
{
public float3 v0;
public float3 v1;
public float3 v2;
};
static Triangle GetTriangle(List<Vector3> vertices, int3 idx)
{
Triangle tri;
tri.v0 = vertices[idx.x];
tri.v1 = vertices[idx.y];
tri.v2 = vertices[idx.z];
return tri;
}
BlockAllocator.Allocation AllocateBlasInternalNodes(int allocationNodeCount)
{
var allocation = m_BlasAllocator.Allocate(allocationNodeCount);
if (!allocation.valid)
{
allocation = m_BlasAllocator.GrowAndAllocate(allocationNodeCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / RadeonRaysAPI.BvhInternalNodeSizeInBytes(), out int oldCapacity, out int newCapacity);
if (!allocation.valid)
return allocation;
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, RadeonRaysAPI.BvhInternalNodeSizeInBytes(), ref m_BlasBuffer);
}
return allocation;
}
BlockAllocator.Allocation AllocateBlasLeafNodes(int allocationNodeCount)
{
var allocation = m_BlasLeavesAllocator.Allocate(allocationNodeCount);
if (!allocation.valid)
{
allocation = m_BlasLeavesAllocator.GrowAndAllocate(allocationNodeCount, GraphicsHelpers.MaxGraphicsBufferSizeInBytes / RadeonRaysAPI.BvhLeafNodeSizeInBytes(), out int oldCapacity, out int newCapacity);
if (!allocation.valid)
return allocation;
GraphicsHelpers.ReallocateBuffer(m_CopyShader, oldCapacity, newCapacity, RadeonRaysAPI.BvhLeafNodeSizeInBytes(), ref m_BlasLeavesBuffer);
}
return allocation;
}
readonly uint m_HandleObfuscation = (uint)Random.Range(int.MinValue, int.MaxValue);
int NewHandle()
{
if (m_FreeHandles.Count != 0)
return (int)(m_FreeHandles.Dequeue() ^ m_HandleObfuscation);
else
return (int)((uint)m_RadeonInstances.Count ^ m_HandleObfuscation);
}
void ReleaseHandle(int handle)
{
m_FreeHandles.Enqueue((uint)handle ^ m_HandleObfuscation);
}
readonly RadeonRaysAPI m_RadeonRaysAPI;
readonly BuildFlags m_BuildFlags;
#if UNITY_EDITOR
readonly bool m_UseCpuBuild;
#endif
readonly ReferenceCounter m_Counter;
readonly Dictionary<(int mesh, int subMeshIndex), MeshBlas> m_Blases;
internal BlockAllocator m_BlasAllocator;
GraphicsBuffer m_BlasBuffer;
internal BlockAllocator m_BlasLeavesAllocator;
GraphicsBuffer m_BlasLeavesBuffer;
readonly BLASPositionsPool m_BlasPositions;
TopLevelAccelStruct? m_TopLevelAccelStruct = null;
readonly ComputeShader m_CopyShader;
readonly Dictionary<int, RadeonRaysInstance> m_RadeonInstances = new ();
readonly Queue<uint> m_FreeHandles = new();
sealed class RadeonRaysInstance
{
public (int mesh, int subMeshIndex) geomKey;
public MeshBlas blas;
public uint instanceMask;
public bool triangleCullingEnabled;
public bool invertTriangleCulling;
public uint userInstanceID;
public RadeonRays.Transform localToWorldTransform;
}
sealed class MeshBlas
{
public MeshBuildInfo buildInfo;
public BlockAllocator.Allocation bvhAlloc;
public BlockAllocator.Allocation bvhLeavesAlloc;
public BlockAllocator.Allocation blasVertices;
#if UNITY_EDITOR
public AABB aabbForCpuBuild;
public List<int> indicesForCpuBuild;
public int baseIndexForCpuBuild;
public List<Vector3> verticesForCpuBuild;
#endif
public bool bvhBuilt = false;
private uint refCount = 0;
public void IncRef() { refCount++; }
public void DecRef() { refCount--; }
public bool IsUnreferenced() { return refCount == 0; }
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingAccelStruct.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47381b95f8bd4f44db5cebb4f2c3cf6d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

29
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingBackend.cs

@ -0,0 +1,29 @@
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal class ComputeRayTracingBackend : IRayTracingBackend
{
public ComputeRayTracingBackend(RayTracingResources resources)
{
m_Resources = resources;
}
public IRayTracingShader CreateRayTracingShader(Object shader, string kernelName, GraphicsBuffer dispatchBuffer)
{
Debug.Assert(shader is ComputeShader);
return new ComputeRayTracingShader((ComputeShader)shader, kernelName, dispatchBuffer);
}
public IRayTracingAccelStruct CreateAccelerationStructure(AccelerationStructureOptions options, ReferenceCounter counter)
{
return new ComputeRayTracingAccelStruct(options, m_Resources, counter);
}
public ulong GetRequiredTraceScratchBufferSizeInBytes(uint width, uint height, uint depth)
{
uint rayCount = width * height * depth;
return (RadeonRays.RadeonRaysAPI.GetTraceMemoryRequirements(rayCount) * RayTracingContext.GetScratchBufferStrideInBytes());
}
readonly RayTracingResources m_Resources;
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingBackend.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 189c94a74c76e8244a89d8dea501dc60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

123
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingShader.cs

@ -0,0 +1,123 @@
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.UnifiedRayTracing
{
internal class ComputeRayTracingShader : IRayTracingShader
{
readonly ComputeShader m_Shader;
readonly int m_KernelIndex;
readonly int m_ComputeIndirectDispatchDimsKernelIndex;
uint3 m_ThreadGroupSizes;
// Temp buffer containing the dispatch dimensions
// For standard dispatches, it contains the dims counted in work groups.
// For indirect dispatches, it contains the dims counted in threads.
readonly GraphicsBuffer m_DispatchBuffer;
internal ComputeRayTracingShader(ComputeShader shader, string dispatchFuncName, GraphicsBuffer dispatchBuffer)
{
m_Shader = shader;
m_KernelIndex = m_Shader.FindKernel(dispatchFuncName);
m_ComputeIndirectDispatchDimsKernelIndex = m_Shader.FindKernel("ComputeIndirectDispatchDims");
m_Shader.GetKernelThreadGroupSizes(m_KernelIndex,
out m_ThreadGroupSizes.x, out m_ThreadGroupSizes.y, out m_ThreadGroupSizes.z);
m_DispatchBuffer = dispatchBuffer;
}
public uint3 GetThreadGroupSizes()
{
return m_ThreadGroupSizes;
}
public void SetAccelerationStructure(CommandBuffer cmd, string name, IRayTracingAccelStruct accelStruct)
{
var computeAccelStruct = accelStruct as ComputeRayTracingAccelStruct;
Assert.IsNotNull(computeAccelStruct);
computeAccelStruct.Bind(cmd, name, this);
}
public void SetIntParam(CommandBuffer cmd, int nameID, int val)
{
cmd.SetComputeIntParam(m_Shader, nameID, val);
}
public void SetFloatParam(CommandBuffer cmd, int nameID, float val)
{
cmd.SetComputeFloatParam(m_Shader, nameID, val);
}
public void SetVectorParam(CommandBuffer cmd, int nameID, Vector4 val)
{
cmd.SetComputeVectorParam(m_Shader, nameID, val);
}
public void SetMatrixParam(CommandBuffer cmd, int nameID, Matrix4x4 val)
{
cmd.SetComputeMatrixParam(m_Shader, nameID, val);
}
public void SetTextureParam(CommandBuffer cmd, int nameID, RenderTargetIdentifier rt)
{
cmd.SetComputeTextureParam(m_Shader, m_KernelIndex, nameID, rt);
}
public void SetBufferParam(CommandBuffer cmd, int nameID, GraphicsBuffer buffer)
{
cmd.SetComputeBufferParam(m_Shader, m_KernelIndex, nameID, buffer);
}
public void SetBufferParam(CommandBuffer cmd, int nameID, ComputeBuffer buffer)
{
cmd.SetComputeBufferParam(m_Shader, m_KernelIndex, nameID, buffer);
}
public void Dispatch(CommandBuffer cmd, GraphicsBuffer scratchBuffer, uint width, uint height, uint depth)
{
var requiredScratchSize = GetTraceScratchBufferRequiredSizeInBytes(width, height, depth);
if (requiredScratchSize > 0)
{
Debug.Assert(scratchBuffer != null && ((ulong)(scratchBuffer.count * scratchBuffer.stride) >= requiredScratchSize), "scratchBuffer size is too small");
Debug.Assert(scratchBuffer.stride == 4, "scratchBuffer stride must be 4");
}
cmd.SetComputeBufferParam(m_Shader, m_KernelIndex, RadeonRays.SID.g_stack, scratchBuffer);
cmd.SetBufferData(m_DispatchBuffer, new uint[] { width, height, depth });
SetBufferParam(cmd, RadeonRays.SID.g_dispatch_dimensions, m_DispatchBuffer);
uint workgroupsX = (uint)GraphicsHelpers.DivUp((int)width, m_ThreadGroupSizes.x);
uint workgroupsY = (uint)GraphicsHelpers.DivUp((int)height, m_ThreadGroupSizes.y);
uint workgroupsZ = (uint)GraphicsHelpers.DivUp((int)depth, m_ThreadGroupSizes.z);
cmd.DispatchCompute(m_Shader, m_KernelIndex, (int)workgroupsX, (int)workgroupsY, (int)workgroupsZ);
}
public void Dispatch(CommandBuffer cmd, GraphicsBuffer scratchBuffer, GraphicsBuffer argsBuffer)
{
SetIndirectDispatchDimensions(cmd, argsBuffer);
DispatchIndirect(cmd, scratchBuffer, argsBuffer);
}
internal void SetIndirectDispatchDimensions(CommandBuffer cmd, GraphicsBuffer argsBuffer)
{
cmd.SetComputeBufferParam(m_Shader, m_ComputeIndirectDispatchDimsKernelIndex, RadeonRays.SID.g_dispatch_dimensions, argsBuffer);
cmd.SetComputeBufferParam(m_Shader, m_ComputeIndirectDispatchDimsKernelIndex, RadeonRays.SID.g_dispatch_dims_in_workgroups, m_DispatchBuffer);
cmd.DispatchCompute(m_Shader, m_ComputeIndirectDispatchDimsKernelIndex, 1, 1, 1);
}
internal void DispatchIndirect(CommandBuffer cmd, GraphicsBuffer scratchBuffer, GraphicsBuffer argsBuffer)
{
cmd.SetComputeBufferParam(m_Shader, m_KernelIndex, RadeonRays.SID.g_stack, scratchBuffer);
cmd.SetComputeBufferParam(m_Shader, m_KernelIndex, RadeonRays.SID.g_dispatch_dimensions, argsBuffer);
cmd.DispatchCompute(m_Shader, m_KernelIndex, m_DispatchBuffer, 0);
}
public ulong GetTraceScratchBufferRequiredSizeInBytes(uint width, uint height, uint depth)
{
uint rayCount = width * height * depth;
return (RadeonRays.RadeonRaysAPI.GetTraceMemoryRequirements(rayCount) * 4);
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRayTracingShader.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2490507baf7aec4d8f25b6e7f7814fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

36
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl

@ -0,0 +1,36 @@
#define WIDTH_OFFSET 0
#define HEIGHT_OFFSET 1
#define DEPTH_OFFSET 2
StructuredBuffer<uint> g_Dimensions;
#ifndef UNIFIED_RT_RAYGEN_FUNC_NAME
#define UNIFIED_RT_RAYGEN_FUNC_NAME RayGenExecute
#endif
#pragma kernel MainRayGenShader
[numthreads(UNIFIED_RT_GROUP_SIZE_X, UNIFIED_RT_GROUP_SIZE_Y, UNIFIED_RT_GROUP_SIZE_Z)]
void MainRayGenShader(
in uint3 gidx: SV_DispatchThreadID,
in uint lidx : SV_GroupIndex)
{
if (gidx.x >= g_Dimensions[WIDTH_OFFSET] || gidx.y >= g_Dimensions[HEIGHT_OFFSET] || gidx.z >= g_Dimensions[DEPTH_OFFSET])
return;
UnifiedRT::DispatchInfo dispatchInfo;
dispatchInfo.dispatchThreadID = gidx;
dispatchInfo.dispatchDimensionsInThreads = int3(g_Dimensions[WIDTH_OFFSET], g_Dimensions[HEIGHT_OFFSET], g_Dimensions[DEPTH_OFFSET]);
dispatchInfo.localThreadIndex = lidx;
dispatchInfo.globalThreadIndex = gidx.x + gidx.y * g_Dimensions[WIDTH_OFFSET] + gidx.z * (g_Dimensions[WIDTH_OFFSET] * g_Dimensions[HEIGHT_OFFSET]);
UNIFIED_RT_RAYGEN_FUNC_NAME(dispatchInfo);
}
RWStructuredBuffer<uint> g_dispatch_dims_in_workgroups;
#pragma kernel ComputeIndirectDispatchDims
[numthreads(3, 1, 1)]
void ComputeIndirectDispatchDims(in uint gidx : SV_DispatchThreadID)
{
uint3 workgroupSizes = uint3(UNIFIED_RT_GROUP_SIZE_X, UNIFIED_RT_GROUP_SIZE_Y, UNIFIED_RT_GROUP_SIZE_Z);
g_dispatch_dims_in_workgroups[gidx] = (g_Dimensions[gidx] + workgroupSizes[gidx] - 1) / workgroupSizes[gidx];
}

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/ComputeRaygenShader.hlsl.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e894eeea9d36e9b4b8d6be81023b7cbe
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays.meta

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7ac9b9b2d31a8ad43adbf58478e79a5f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

312
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/BvhCheck.cs

@ -0,0 +1,312 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering.RadeonRays
{
internal class AABB
{
public float3 Min;
public float3 Max;
public AABB()
{
Min = new float3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
Max = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
}
public AABB(float3 min, float3 max)
{
Min = min;
Max = max;
}
public void Encapsulate(AABB aabb)
{
Min = math.min(Min, aabb.Min);
Max = math.max(Max, aabb.Max);
}
public void Encapsulate(float3 point)
{
Min = math.min(Min, point);
Max = math.max(Max, point);
}
public bool Contains(AABB rhs)
{
return rhs.Min.x >= Min.x && rhs.Min.y >= Min.y && rhs.Min.z >= Min.z &&
rhs.Max.x <= Max.x && rhs.Max.y <= Max.y && rhs.Max.z <= Max.z;
}
public bool IsValid()
{
return Min.x <= Max.x && Min.y <= Max.y && Min.z <= Max.z;
}
}
internal class BvhCheck
{
const uint kInvalidID = ~0u;
public class VertexBuffers
{
public GraphicsBuffer vertices;
public GraphicsBuffer indices;
public uint vertexBufferOffset = 0;
public uint vertexCount;
public uint vertexStride = 3;
public uint indexBufferOffset = 0;
public IndexFormat indexFormat = IndexFormat.Int32;
public uint indexCount;
};
public static VertexBuffers Convert(MeshBuildInfo info)
{
var res = new VertexBuffers()
{
vertices = info.vertices,
indices = info.triangleIndices,
vertexBufferOffset = (uint)info.verticesStartOffset,
vertexCount = info.vertexCount,
vertexStride = info.vertexStride,
indexBufferOffset = (uint)info.indicesStartOffset,
indexCount = info.triangleCount * 3,
indexFormat = info.indexFormat
};
return res;
}
public static double SurfaceArea(AABB aabb)
{
float3 edges = aabb.Max - aabb.Min;
return 2.0f * (edges.x * edges.y + edges.x * edges.z + edges.z * edges.y);
}
public static double NodeSahCost(uint nodeAddr, AABB nodeAabb, AABB parentAabb)
{
double cost = IsLeafNode(nodeAddr) ? GetLeafNodePrimCount(nodeAddr) : 1.2f;
var a = SurfaceArea(nodeAabb);
var b = SurfaceArea(parentAabb);
return cost * a/ b;
}
public static double CheckConsistency(VertexBuffers bvhVertexBuffers, BottomLevelLevelAccelStruct bvh, uint primitiveCount)
{
return CheckConsistency(bvhVertexBuffers, bvh.bvh, bvh.bvhOffset, bvh.bvhLeaves, bvh.bvhLeavesOffset, primitiveCount);
}
public static double CheckConsistency(GraphicsBuffer bvhBuffer, uint bvhBufferOffset, uint primitiveCount)
{
return CheckConsistency(null, bvhBuffer, bvhBufferOffset, null, 0, primitiveCount);
}
static double CheckConsistency(
VertexBuffers bvhVertexBuffers,
GraphicsBuffer bvhBuffer, uint bvhBufferOffset, GraphicsBuffer bvhLeavesBuffer, uint bvhLeavesBufferOffset,
uint primitiveCount)
{
var header = new BvhHeader[1];
bvhBuffer.GetData(header, 0, (int)bvhBufferOffset, 1);
return CheckConsistency(bvhVertexBuffers, bvhBuffer, bvhBufferOffset + 1, bvhLeavesBuffer, bvhLeavesBufferOffset, header[0], primitiveCount);
}
public static int ExtractBits(uint value, int startBit, int count)
{
uint mask = (uint)(((1 << count) - 1) << startBit);
return ((int)(mask & value)) >> startBit;
}
public static bool IsLeafNode(uint nodeAddr)
{
return (nodeAddr & (1 << 31)) != 0;
}
public static uint GetLeafNodeFirstPrim(uint nodeAddr)
{
return (nodeAddr & ~0xE0000000);
}
public static uint GetLeafNodePrimCount(uint nodeAddr)
{
return (uint)ExtractBits(nodeAddr, 29, 2) + 1;
}
static double CheckConsistency(
VertexBuffers bvhVertexBuffers,
GraphicsBuffer bvhBuffer, uint bvhBufferOffset, GraphicsBuffer bvhLeavesBuffer, uint bvhLeavesBufferOffset,
BvhHeader header, uint primitiveCount)
{
uint leafCount = header.leafNodeCount;
uint rootAddr = header.root;
var nodeCount = HlbvhBuilder.GetBvhNodeCount(leafCount);
bool isTopLevel = bvhVertexBuffers == null;
var bvhNodes = new BvhNode[nodeCount];
bvhBuffer.GetData(bvhNodes, 0, (int)bvhBufferOffset, (int)nodeCount);
VertexBuffersCPU vertexBuffers = null;
uint4[] bvhLeafNodes = null;
if (!isTopLevel)
{
vertexBuffers = DownloadVertexData(bvhVertexBuffers);
bvhLeafNodes = new uint4[primitiveCount];
bvhLeavesBuffer.GetData(bvhLeafNodes, 0, (int)bvhLeavesBufferOffset, (int)primitiveCount);
}
uint countedPrimitives = 0;
var rootAabb = GetAabb(vertexBuffers, bvhNodes, bvhLeafNodes, rootAddr, isTopLevel);
double sahCost = 0.0f;
var q = new Queue<(uint Addr, uint Parent)>();
q.Enqueue((Addr: rootAddr, Parent: kInvalidID));
while (q.Count != 0)
{
var current = q.Dequeue();
uint addr = current.Addr;
uint parent = current.Parent;
AABB aabb = GetAabb(vertexBuffers, bvhNodes, bvhLeafNodes, addr, isTopLevel);
sahCost += NodeSahCost(addr, aabb, rootAabb);
if (!(isTopLevel && IsLeafNode(addr)))
Assert.IsTrue(aabb.IsValid());
if (IsLeafNode(addr))
{
countedPrimitives += isTopLevel ? 1 : GetLeafNodePrimCount(addr);
}
else // internal node
{
var node = bvhNodes[addr];
Assert.AreEqual(parent, node.parent);
var leftAabb = GetAabb(vertexBuffers, bvhNodes, bvhLeafNodes, node.child0, isTopLevel);
var rightAabb = GetAabb(vertexBuffers, bvhNodes, bvhLeafNodes, node.child1, isTopLevel);
bool leftOk = (aabb.Contains(leftAabb));
bool rightOk = (aabb.Contains(rightAabb));
Assert.IsTrue(leftOk);
Assert.IsTrue(rightOk);
q.Enqueue((Addr: node.child0, Parent: addr));
q.Enqueue((Addr: node.child1, Parent: addr));
}
}
Assert.AreEqual(countedPrimitives, primitiveCount);
return sahCost;
}
private sealed class VertexBuffersCPU
{
public float[] vertices;
public uint[] indices;
public uint vertexStride;
};
static uint3 GetFaceIndices(uint[] indices, uint triangleIdx)
{
return new uint3(
indices[3 * triangleIdx],
indices[3 * triangleIdx + 1],
indices[3 * triangleIdx + 2]);
}
static float3 GetVertex(float[] vertices, uint stride, uint idx)
{
uint indexInFloats = idx * stride;
return new float3(
vertices[indexInFloats],
vertices[indexInFloats + 1],
vertices[indexInFloats + 2]);
}
struct Triangle
{
public float3 v0;
public float3 v1;
public float3 v2;
};
static Triangle GetTriangle(float[] vertices, uint stride, uint3 idx)
{
Triangle tri;
tri.v0 = GetVertex(vertices, stride, idx.x);
tri.v1 = GetVertex(vertices, stride, idx.y);
tri.v2 = GetVertex(vertices, stride, idx.z);
return tri;
}
static VertexBuffersCPU DownloadVertexData(VertexBuffers vertexBuffers)
{
var result = new VertexBuffersCPU();
result.vertices = new float[vertexBuffers.vertexCount * vertexBuffers.vertexStride];
result.indices = new uint[vertexBuffers.indexCount];
result.vertexStride = vertexBuffers.vertexStride;
if (vertexBuffers.indexFormat == IndexFormat.Int32)
{
vertexBuffers.indices.GetData(result.indices, 0, (int)vertexBuffers.indexBufferOffset, (int)vertexBuffers.indexCount);
}
else
{
var tmp = new ushort[vertexBuffers.indexCount];
vertexBuffers.indices.GetData(tmp, 0, (int)vertexBuffers.indexBufferOffset, (int)vertexBuffers.indexCount);
for (int i = 0; i < vertexBuffers.indexCount; ++i)
result.indices[i] = tmp[i];
}
vertexBuffers.vertices.GetData(result.vertices, 0, (int)vertexBuffers.vertexBufferOffset, (int)(vertexBuffers.vertexCount * vertexBuffers.vertexStride));
return result;
}
static AABB GetAabb(VertexBuffersCPU bvhVertexBuffers, BvhNode[] bvhNodes, uint4[] bvhLeafNodes, uint nodeAddr, bool isTopLevel)
{
var aabb = new AABB();
if (!IsLeafNode(nodeAddr))
{
var node = bvhNodes[nodeAddr];
AABB left = new AABB(node.aabb0_min, node.aabb0_max);
aabb.Encapsulate(left);
AABB right = new AABB(node.aabb1_min, node.aabb1_max);
aabb.Encapsulate(right);
}
else if (!isTopLevel)
{
int firstIndex = (int)GetLeafNodeFirstPrim(nodeAddr);
int triangleCount = (int)GetLeafNodePrimCount(nodeAddr);
for (int i = 0; i < triangleCount; ++i)
{
uint index = (uint)(i + firstIndex);
uint3 triangleIndices = bvhLeafNodes[index].xyz;
uint3 meshTriangleindices = GetFaceIndices(bvhVertexBuffers.indices, bvhLeafNodes[index].w);
Assert.AreEqual(meshTriangleindices, triangleIndices);
var triangle = GetTriangle(bvhVertexBuffers.vertices, bvhVertexBuffers.vertexStride, triangleIndices);
aabb.Encapsulate(triangle.v0);
aabb.Encapsulate(triangle.v1);
aabb.Encapsulate(triangle.v2);
}
}
return aabb;
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/BvhCheck.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 948c469ea5274f340a6aaa3e9719c7a1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

23
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/Common.cs

@ -0,0 +1,23 @@
namespace UnityEngine.Rendering.RadeonRays
{
internal static class Common
{
public static uint CeilDivide(uint val, uint div)
{
return (val + div - 1) / div;
}
public static void EnableKeyword(CommandBuffer cmd, ComputeShader shader, string keyword, bool enable)
{
if (enable)
{
cmd.EnableKeyword(shader, new LocalKeyword(shader, keyword));
}
else
{
cmd.DisableKeyword(shader, new LocalKeyword(shader, keyword));
}
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/Common.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f08443d04942ec5498c71b7961ecbf75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

162
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhBuilder.cs

@ -0,0 +1,162 @@
using System;
using System.Runtime.InteropServices;
using Unity.Mathematics;
namespace UnityEngine.Rendering.RadeonRays
{
internal struct BottomLevelLevelAccelStruct
{
public GraphicsBuffer bvh;
public uint bvhOffset;
public GraphicsBuffer bvhLeaves;
public uint bvhLeavesOffset;
}
internal class HlbvhBuilder
{
readonly ComputeShader shaderBuildHlbvh;
readonly int kernelInit;
readonly int kernelCalculateAabb;
readonly int kernelCalculateMortonCodes;
readonly int kernelBuildTreeBottomUp;
readonly RadixSort radixSort;
const uint kTrianglesPerThread = 8u;
const uint kGroupSize = 256u;
const uint kTrianglesPerGroup = kTrianglesPerThread * kGroupSize;
public HlbvhBuilder(RadeonRaysShaders shaders)
{
shaderBuildHlbvh = shaders.buildHlbvh;
kernelInit = shaderBuildHlbvh.FindKernel("Init");
kernelCalculateAabb = shaderBuildHlbvh.FindKernel("CalculateAabb");
kernelCalculateMortonCodes = shaderBuildHlbvh.FindKernel("CalculateMortonCodes");
kernelBuildTreeBottomUp = shaderBuildHlbvh.FindKernel("BuildTreeBottomUp");
radixSort = new RadixSort(shaders);
}
public uint GetScratchDataSizeInDwords(uint triangleCount)
{
var scratchLayout = ScratchBufferLayout.Create(triangleCount);
return scratchLayout.TotalSize;
}
public static uint GetBvhNodeCount(uint leafCount)
{
return leafCount - 1;
}
public uint GetResultDataSizeInDwords(uint triangleCount)
{
var bvhNodeCount = GetBvhNodeCount(triangleCount) + 1; // plus one for header
uint sizeOfNode = 16;
return bvhNodeCount * sizeOfNode;
}
public void Execute(
CommandBuffer cmd,
GraphicsBuffer vertices, int verticesOffset, uint vertexStride,
GraphicsBuffer indices, int indicesOffset, int baseIndex, IndexFormat indexFormat, uint triangleCount,
GraphicsBuffer scratch,
in BottomLevelLevelAccelStruct result)
{
Common.EnableKeyword(cmd, shaderBuildHlbvh, "TOP_LEVEL", false);
Common.EnableKeyword(cmd, shaderBuildHlbvh, "UINT16_INDICES", indexFormat == IndexFormat.Int16);
var scratchLayout = ScratchBufferLayout.Create(triangleCount);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_indices_offset, indicesOffset);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_base_index, baseIndex);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_vertices_offset, verticesOffset);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_vertex_stride, (int)vertexStride);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_triangle_count, (int)triangleCount);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_bvh_offset, (int)result.bvhOffset);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_bvh_leaves_offset, (int)result.bvhLeavesOffset);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_internal_node_range_offset, (int)scratchLayout.InternalNodeRange);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_leaf_parents_offset, (int)scratchLayout.LeafParents);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_aabb_offset, (int)scratchLayout.Aabb);
BindKernelArguments(cmd, kernelInit, vertices, indices, scratch, scratchLayout, result, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelInit, 1, 1, 1);
BindKernelArguments(cmd, kernelCalculateAabb, vertices, indices, scratch, scratchLayout, result, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateAabb, (int)Common.CeilDivide(triangleCount, kTrianglesPerGroup), 1, 1);
BindKernelArguments(cmd, kernelCalculateMortonCodes, vertices, indices, scratch, scratchLayout, result, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateMortonCodes, (int)Common.CeilDivide(triangleCount, kTrianglesPerGroup), 1, 1);
radixSort.Execute(cmd, scratch,
scratchLayout.MortonCodes, scratchLayout.SortedMortonCodes,
scratchLayout.PrimitiveRefs, scratchLayout.SortedPrimitiveRefs,
scratchLayout.SortMemory, triangleCount);
BindKernelArguments(cmd, kernelBuildTreeBottomUp, vertices, indices, scratch, scratchLayout, result, true);
cmd.DispatchCompute(shaderBuildHlbvh, kernelBuildTreeBottomUp, (int)Common.CeilDivide(triangleCount, kTrianglesPerGroup), 1, 1);
}
private void BindKernelArguments(
CommandBuffer cmd,
int kernel,
GraphicsBuffer vertices,
GraphicsBuffer indices,
GraphicsBuffer scratch,
ScratchBufferLayout scratchLayout,
BottomLevelLevelAccelStruct result,
bool setSortedCodes)
{
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_vertices, vertices);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_indices, indices);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_scratch_buffer, scratch);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bvh, result.bvh);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bvh_leaves, result.bvhLeaves);
if (setSortedCodes)
{
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.SortedMortonCodes);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.SortedPrimitiveRefs);
}
else
{
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.MortonCodes);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.PrimitiveRefs);
}
}
struct ScratchBufferLayout
{
public uint PrimitiveRefs;
public uint MortonCodes;
public uint SortedPrimitiveRefs;
public uint SortedMortonCodes;
public uint SortMemory;
public uint Aabb;
public uint LeafParents;
public uint InternalNodeRange;
public uint TotalSize;
public static ScratchBufferLayout Create(uint triangleCount)
{
var result = new ScratchBufferLayout();
result.SortMemory = result.Reserve(math.max((uint)RadixSort.GetScratchDataSizeInDwords(triangleCount), triangleCount));
result.PrimitiveRefs = result.Reserve(triangleCount);
result.MortonCodes = result.Reserve(triangleCount);
result.SortedPrimitiveRefs = result.Reserve(triangleCount);
result.SortedMortonCodes = result.Reserve(triangleCount);
result.Aabb = result.Reserve(6);
result.InternalNodeRange = result.PrimitiveRefs;
result.LeafParents = result.SortMemory;
return result;
}
uint Reserve(uint size)
{
var temp = TotalSize;
TotalSize += size;
return temp;
}
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhBuilder.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7aeb48182611e8e4898e4bf90c4db57b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

187
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhTopLevelBuilder.cs

@ -0,0 +1,187 @@
using System;
using System.Runtime.InteropServices;
using Unity.Mathematics;
namespace UnityEngine.Rendering.RadeonRays
{
internal struct TopLevelAccelStruct : IDisposable
{
public const GraphicsBuffer.Target topLevelBvhTarget = GraphicsBuffer.Target.Structured;
public const GraphicsBuffer.Target instanceInfoTarget = GraphicsBuffer.Target.Structured;
public GraphicsBuffer topLevelBvh;
public GraphicsBuffer bottomLevelBvhs;
public GraphicsBuffer instanceInfos;
public uint instanceCount;
public void Dispose()
{
topLevelBvh?.Dispose();
instanceInfos?.Dispose();
}
}
internal class HlbvhTopLevelBuilder
{
readonly ComputeShader shaderBuildHlbvh;
readonly int kernelInit;
readonly int kernelCalculateAabb;
readonly int kernelCalculateMortonCodes;
readonly int kernelBuildTreeBottomUp;
readonly RadixSort radixSort;
const uint kTrianglesPerThread = 8u;
const uint kGroupSize = 256u;
const uint kTrianglesPerGroup = kTrianglesPerThread * kGroupSize;
public HlbvhTopLevelBuilder(RadeonRaysShaders shaders)
{
shaderBuildHlbvh = shaders.buildHlbvh;
kernelInit = shaderBuildHlbvh.FindKernel("Init");
kernelCalculateAabb = shaderBuildHlbvh.FindKernel("CalculateAabb");
kernelCalculateMortonCodes = shaderBuildHlbvh.FindKernel("CalculateMortonCodes");
kernelBuildTreeBottomUp = shaderBuildHlbvh.FindKernel("BuildTreeBottomUp");
radixSort = new RadixSort(shaders);
}
public ulong GetScratchDataSizeInDwords(uint instanceCount)
{
var scratchLayout = ScratchBufferLayout.Create(instanceCount);
return scratchLayout.TotalSize;
}
public static uint GetBvhNodeCount(uint leafCount)
{
return leafCount - 1;
}
public void AllocateResultBuffers(uint instanceCount, ref TopLevelAccelStruct accelStruct)
{
var bvhNodeCount = GetBvhNodeCount(instanceCount);
accelStruct.Dispose();
accelStruct.instanceInfos = new GraphicsBuffer(TopLevelAccelStruct.instanceInfoTarget, (int)instanceCount, Marshal.SizeOf<InstanceInfo>());
accelStruct.topLevelBvh = new GraphicsBuffer(TopLevelAccelStruct.topLevelBvhTarget, (int)bvhNodeCount + 1, Marshal.SizeOf<BvhNode>()); // plus one for header
}
public void CreateEmpty(ref TopLevelAccelStruct accelStruct)
{
accelStruct.Dispose();
accelStruct.topLevelBvh = new GraphicsBuffer(TopLevelAccelStruct.topLevelBvhTarget, 2, Marshal.SizeOf<BvhNode>());
accelStruct.instanceInfos = accelStruct.topLevelBvh;
accelStruct.bottomLevelBvhs = accelStruct.topLevelBvh;
accelStruct.instanceCount = 0;
var top = new BvhNode[2];
top[0].child0 = 0;
top[0].child1 = 0;
top[0].parent = 0;
top[1].child0 = 0;
top[1].child1 = 0;
top[1].parent = 0xFFFFFFFF;
top[1].update = 0;
top[1].aabb0_min = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
top[1].aabb0_max = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
top[1].aabb1_min = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
top[1].aabb1_max = new float3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
accelStruct.topLevelBvh.SetData(top);
}
public void Execute(CommandBuffer cmd, GraphicsBuffer scratch, ref TopLevelAccelStruct accelStruct)
{
Common.EnableKeyword(cmd, shaderBuildHlbvh, "TOP_LEVEL", true);
Common.EnableKeyword(cmd, shaderBuildHlbvh, "UINT16_INDICES", false);
uint instanceCount = accelStruct.instanceCount;
var scratchLayout = ScratchBufferLayout.Create(instanceCount);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_vertex_stride, 0);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_constants_triangle_count, (int)instanceCount);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_bvh_offset, 0);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_internal_node_range_offset, (int)scratchLayout.InternalNodeRange);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_aabb_offset, (int)scratchLayout.Aabb);
BindKernelArguments(cmd, kernelInit, scratch, scratchLayout, accelStruct, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelInit, 1, 1, 1);
BindKernelArguments(cmd, kernelCalculateAabb, scratch, scratchLayout, accelStruct, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateAabb, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1);
BindKernelArguments(cmd, kernelCalculateMortonCodes, scratch, scratchLayout, accelStruct, false);
cmd.DispatchCompute(shaderBuildHlbvh, kernelCalculateMortonCodes, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1);
radixSort.Execute(cmd, scratch,
scratchLayout.MortonCodes, scratchLayout.SortedMortonCodes,
scratchLayout.PrimitiveRefs, scratchLayout.SortedPrimitiveRefs,
scratchLayout.SortMemory, instanceCount);
BindKernelArguments(cmd, kernelBuildTreeBottomUp, scratch, scratchLayout, accelStruct, true);
cmd.DispatchCompute(shaderBuildHlbvh, kernelBuildTreeBottomUp, (int)Common.CeilDivide(instanceCount, kTrianglesPerGroup), 1, 1);
}
struct ScratchBufferLayout
{
public uint Aabb;
public uint MortonCodes;
public uint PrimitiveRefs;
public uint SortedMortonCodes;
public uint SortedPrimitiveRefs;
public uint SortMemory;
public uint InternalNodeRange;
public uint TotalSize;
public static ScratchBufferLayout Create(uint instanceCount)
{
var result = new ScratchBufferLayout();
result.Aabb = result.Reserve(6);
result.MortonCodes = result.Reserve(instanceCount);
result.PrimitiveRefs = result.Reserve(instanceCount);
result.SortedMortonCodes = result.Reserve(instanceCount);
result.SortedPrimitiveRefs = result.Reserve(instanceCount);
result.SortMemory = result.Reserve((uint)RadixSort.GetScratchDataSizeInDwords(instanceCount));
// overlaps with MortonCodes and PrimitiveRefs
result.InternalNodeRange = result.MortonCodes;
return result;
}
uint Reserve(uint size)
{
var temp = TotalSize;
TotalSize += size;
return temp;
}
}
private void BindKernelArguments(
CommandBuffer cmd,
int kernel,
GraphicsBuffer scratch,
ScratchBufferLayout scratchLayout,
TopLevelAccelStruct accelStruct,
bool setSortedCodes)
{
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_scratch_buffer, scratch);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bvh, accelStruct.topLevelBvh);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_bottom_bvhs, accelStruct.bottomLevelBvhs);
cmd.SetComputeBufferParam(shaderBuildHlbvh, kernel, SID.g_instance_infos, accelStruct.instanceInfos);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_aabb_offset, (int)scratchLayout.Aabb);
if (setSortedCodes)
{
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.SortedMortonCodes);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.SortedPrimitiveRefs);
}
else
{
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_morton_codes_offset, (int)scratchLayout.MortonCodes);
cmd.SetComputeIntParam(shaderBuildHlbvh, SID.g_primitive_refs_offset, (int)scratchLayout.PrimitiveRefs);
}
}
}
}

11
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/HlbvhTopLevelBuilder.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5bfccd437a0363140896a3d2f3bc9492
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

5
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/README.txt

@ -0,0 +1,5 @@
This code has been ported from C++ to C# from the RadeonRays library. Version used: 4.1 release (https://github.com/GPUOpen-LibrariesAndSDKs/RadeonRays_SDK)
The files follow the same structure and naming as their original counterpart in https://github.com/GPUOpen-LibrariesAndSDKs/RadeonRays_SDK/tree/master/src/core/src/dx.
- Modifications have been done to the HLSL shaders to work around bugs found in the old FXC compiler.
- See comments in HlbvhBuilder.cs for modifications done to code responsible for the BVH build.

7
Packages/com.unity.rendering.light-transport/Runtime/UnifiedRayTracing/Compute/RadeonRays/README.txt.meta

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f278ca56cfaafbd43be8c0013c5224f5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save