From 00fe9750dc2e108593c80b3eacc768531d491619 Mon Sep 17 00:00:00 2001 From: Elliot Cuzzillo Date: Tue, 13 Sep 2022 09:21:41 -0700 Subject: [PATCH] burstify queries and calculating velocities, and move many things to native collections for that purpose --- .../Collision/OceanSampleDisplacementDemo.cs | 21 +- .../Crest/Scripts/Collision/CollProvider.cs | 17 +- .../Scripts/Collision/CollProviderBakedFFT.cs | 49 ++-- .../Scripts/Collision/CollProviderNull.cs | 16 +- .../Crest/Scripts/Collision/FlowProvider.cs | 7 +- .../Scripts/Collision/FlowProviderNull.cs | 6 +- .../Crest/Scripts/Collision/QueryBase.cs | 237 +++++++++++++----- .../Scripts/Collision/QueryDisplacements.cs | 15 +- .../Crest/Scripts/Collision/QueryFlow.cs | 11 +- .../Crest/Scripts/Collision/RayTraceHelper.cs | 28 ++- .../Scripts/Collision/SamplingHelpers.cs | 170 +++++++++++-- .../Collision/VisualiseCollisionArea.cs | 32 +-- crest/Assets/Crest/Crest/Scripts/Crest.asmdef | 5 +- .../Crest/Scripts/Interaction/BoatProbes.cs | 16 +- .../Crest/Crest/Scripts/LodData/LodDataMgr.cs | 5 +- crest/Packages/manifest.json | 1 + 16 files changed, 475 insertions(+), 161 deletions(-) diff --git a/crest/Assets/Crest/Crest-Examples/Shared/Scripts/Collision/OceanSampleDisplacementDemo.cs b/crest/Assets/Crest/Crest-Examples/Shared/Scripts/Collision/OceanSampleDisplacementDemo.cs index 0a4587a65..d3bac3444 100644 --- a/crest/Assets/Crest/Crest-Examples/Shared/Scripts/Collision/OceanSampleDisplacementDemo.cs +++ b/crest/Assets/Crest/Crest-Examples/Shared/Scripts/Collision/OceanSampleDisplacementDemo.cs @@ -2,7 +2,9 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using System; using Crest; +using Unity.Collections; using UnityEngine; /// @@ -26,13 +28,22 @@ public class OceanSampleDisplacementDemo : MonoBehaviour public float _minGridSize = 0f; GameObject[] _markerObjects = new GameObject[3]; - Vector3[] _markerPos = new Vector3[3]; - Vector3[] _resultDisps = new Vector3[3]; - Vector3[] _resultNorms = new Vector3[3]; - Vector3[] _resultVels = new Vector3[3]; + NativeArray _markerPos; + NativeArray _resultDisps; + NativeArray _resultNorms; + NativeArray _resultVels; float _samplesRadius = 5f; + private void Start() + { + _markerPos = new NativeArray(3, Allocator.Persistent); + _resultDisps = new NativeArray(3, Allocator.Persistent); + _resultNorms = new NativeArray(3, Allocator.Persistent); + _resultVels = new NativeArray(3, Allocator.Persistent); + + } + void Update() { if (OceanRenderer.Instance == null) @@ -52,7 +63,7 @@ void Update() var collProvider = OceanRenderer.Instance.CollisionProvider; - var status = collProvider.Query(GetHashCode(), _minGridSize, _markerPos, _resultDisps, _resultNorms, _resultVels); + var status = collProvider.Query(GetHashCode(), _minGridSize, ref _markerPos, ref _resultDisps, ref _resultNorms, ref _resultVels, true); if (collProvider.RetrieveSucceeded(status)) { diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/CollProvider.cs b/crest/Assets/Crest/Crest/Scripts/Collision/CollProvider.cs index d3605c19b..47e3201f1 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/CollProvider.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/CollProvider.cs @@ -4,6 +4,7 @@ // NOTE: DWP2 depends on this file. Any API changes need to be communicated to the DWP2 authors in advance. +using Unity.Collections; using UnityEngine; namespace Crest @@ -22,7 +23,12 @@ public interface ICollProvider /// Float array of water heights at the query positions. Pass null if this information is not required. /// Water normals at the query positions. Pass null if this information is not required. /// Water surface velocities at the query positions. Pass null if this information is not required. - int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels); + int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultHeights, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels); /// /// Query water physical data at a set of points. Pass in null to any out parameters that are not required. @@ -33,7 +39,14 @@ public interface ICollProvider /// Displacement vectors for water surface points that will displace to the XZ coordinates of the query points. Water heights are given by sea level plus the y component of the displacement. /// Water normals at the query positions. Pass null if this information is not required. /// Water surface velocities at the query positions. Pass null if this information is not required. - int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels); + /// + int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultDisps, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels, + bool useNormals); /// /// Check if query results could be retrieved successfully using return code from Query() function diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderBakedFFT.cs b/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderBakedFFT.cs index da919d51d..f0d2993ef 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderBakedFFT.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderBakedFFT.cs @@ -72,7 +72,9 @@ class QueryData /// position data will be created. This will force any running jobs to complete. The jobs will be kicked off in LateUpdate, /// so this should be called before the kick-off, such as from Update. /// - public int RegisterQueryPoints(int ownerHash, Vector3[] queryPoints, int dataToWriteThisFrame) + public int RegisterQueryPoints(int ownerHash, + ref NativeArray queryPoints, + int dataToWriteThisFrame) { var numQuads = (queryPoints.Length + 3) / 4; @@ -222,7 +224,7 @@ public CollProviderBakedFFT(FFTBakedData data) } } - bool RetrieveHeights(int i_ownerHash, float[] o_resultHeights) + bool RetrieveHeights(int i_ownerHash, NativeArray o_resultHeights) { // Return data - get segment from finished jobs if (o_resultHeights != null && _queryDataHeights._segmentRegistryQueriesResults.TryGetValue(i_ownerHash, out var computedQuerySegment)) @@ -238,7 +240,7 @@ bool RetrieveHeights(int i_ownerHash, float[] o_resultHeights) return false; } - bool RetrieveDisps(int i_ownerHash, Vector3[] o_resultDisps) + bool RetrieveDisps(int i_ownerHash, NativeArray o_resultDisps) { // Return data - get segment from finished jobs if (o_resultDisps != null && _queryDataDisps._segmentRegistryQueriesResults.TryGetValue(i_ownerHash, out var computedQuerySegment)) @@ -258,7 +260,7 @@ bool RetrieveDisps(int i_ownerHash, Vector3[] o_resultDisps) return false; } - bool RetrieveNorms(int i_ownerHash, Vector3[] o_resultNorms) + bool RetrieveNorms(int i_ownerHash, NativeArray o_resultNorms) { if (o_resultNorms != null && _queryDataNorms._segmentRegistryQueriesResults.TryGetValue(i_ownerHash, out var computedQuerySegment)) { @@ -277,7 +279,7 @@ bool RetrieveNorms(int i_ownerHash, Vector3[] o_resultNorms) return false; } - bool RetrieveVels(int i_ownerHash, Vector3[] o_resultVels) + bool RetrieveVels(int i_ownerHash, NativeArray o_resultVels) { if (o_resultVels != null && _queryDataVels._segmentRegistryQueriesResults.TryGetValue(i_ownerHash, out var computedQuerySegment)) { @@ -294,14 +296,12 @@ bool RetrieveVels(int i_ownerHash, Vector3[] o_resultVels) return false; } - public int Query( - int i_ownerHash, + public int Query(int i_ownerHash, float i_minSpatialLength, - Vector3[] i_queryPoints, - float[] o_resultHeights, - Vector3[] o_resultNorms, - Vector3[] o_resultVels - ) + ref NativeArray i_queryPoints, + ref NativeArray o_resultHeights, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels) { var dataCopiedOutHeights = RetrieveHeights(i_ownerHash, o_resultHeights); var dataCopiedOutNorms = RetrieveNorms(i_ownerHash, o_resultNorms); @@ -309,15 +309,15 @@ Vector3[] o_resultVels if (o_resultHeights != null) { - _queryDataHeights.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataHeights.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } if (o_resultNorms != null) { - _queryDataNorms.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataNorms.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } if (o_resultVels != null) { - _queryDataVels.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataVels.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } var allCopied = (dataCopiedOutHeights || o_resultHeights == null) @@ -327,14 +327,13 @@ Vector3[] o_resultVels return allCopied ? (int)QueryStatus.Success : (int)QueryStatus.ResultsNotReadyYet; } - public int Query( - int i_ownerHash, + public int Query(int i_ownerHash, float i_minSpatialLength, - Vector3[] i_queryPoints, - Vector3[] o_resultDisps, - Vector3[] o_resultNorms, - Vector3[] o_resultVels - ) + ref NativeArray i_queryPoints, + ref NativeArray o_resultDisps, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels, + bool useNormals) { var dataCopiedOutDisps = RetrieveDisps(i_ownerHash, o_resultDisps); var dataCopiedOutNorms = RetrieveNorms(i_ownerHash, o_resultNorms); @@ -342,15 +341,15 @@ Vector3[] o_resultVels if (o_resultDisps != null) { - _queryDataDisps.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataDisps.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } if (o_resultNorms != null) { - _queryDataNorms.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataNorms.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } if (o_resultVels != null) { - _queryDataVels.RegisterQueryPoints(i_ownerHash, i_queryPoints, 1 - _dataBeingUsedByJobs); + _queryDataVels.RegisterQueryPoints(i_ownerHash, ref i_queryPoints, 1 - _dataBeingUsedByJobs); } var allCopied = (dataCopiedOutDisps || o_resultDisps == null) diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderNull.cs b/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderNull.cs index cb8181efb..7781b36bc 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderNull.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/CollProviderNull.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -11,7 +12,13 @@ namespace Crest /// public class CollProviderNull : ICollProvider { - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultDisps, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels, + bool useNormals) { if (o_resultDisps != null) { @@ -40,7 +47,12 @@ public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoi return 0; } - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultHeights, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels) { if (o_resultHeights != null) { diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/FlowProvider.cs b/crest/Assets/Crest/Crest/Scripts/Collision/FlowProvider.cs index 024941e9d..39ff5cd95 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/FlowProvider.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/FlowProvider.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -17,8 +18,12 @@ public interface IFlowProvider /// Unique ID for calling code. Typically acquired by calling GetHashCode(). /// The min spatial length of the object, such as the width of a boat. Useful for filtering out detail when not needed. Set to 0 to get full available detail. /// The world space points that will be queried. + /// /// Water surface flow velocities at the query positions. - int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultFlows); + int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultFlows); /// /// Check if query results could be retrieved successfully using return code from Query() function diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/FlowProviderNull.cs b/crest/Assets/Crest/Crest/Scripts/Collision/FlowProviderNull.cs index bba826696..855353258 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/FlowProviderNull.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/FlowProviderNull.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -11,7 +12,10 @@ namespace Crest /// public class FlowProviderNull : IFlowProvider { - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultFlows) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultFlows) { if (o_resultFlows != null) { diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/QueryBase.cs b/crest/Assets/Crest/Crest/Scripts/Collision/QueryBase.cs index cfd454f04..79ef839ed 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/QueryBase.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/QueryBase.cs @@ -7,7 +7,9 @@ // - Half minGridSize using System.Collections.Generic; +using Unity.Burst; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Rendering; @@ -18,6 +20,7 @@ namespace Crest /// the data and then transferring back the results asynchronously. An exception to this is water surface velocities - these can /// not be computed on the GPU and are instead computed on the CPU by retaining last frames' query results and computing finite diffs. /// + [BurstCompile] public abstract class QueryBase { protected int _kernelHandle; @@ -49,16 +52,16 @@ public abstract class QueryBase public const int MAX_QUERY_COUNT_DEFAULT = 4096; int _maxQueryCount = MAX_QUERY_COUNT_DEFAULT; - Vector3[] _queryPosXZ_minGridSize = new Vector3[MAX_QUERY_COUNT_DEFAULT]; + NativeArray _queryPosXZ_minGridSize; /// /// Holds information about all query points. Maps from unique hash code to position in point array. /// - class SegmentRegistrar + struct SegmentRegistrar { // Map from guids to (segment start index, segment end index, frame number when query was made) - public Dictionary _segments = new Dictionary(); - public int _numQueries = 0; + public NativeParallelHashMap _segments; + public int _numQueries; } /// @@ -70,18 +73,20 @@ class SegmentRegistrarRingBuffer // Requests in flight plus 2 held values, plus one current readonly static int s_poolSize = s_maxRequests + 2 + 1; - SegmentRegistrar[] _segments = new SegmentRegistrar[s_poolSize]; + SegmentRegistrar[] _segments; public int _segmentRelease = 0; public int _segmentAcquire = 0; - public SegmentRegistrar Current => _segments[_segmentAcquire]; + public ref SegmentRegistrar Current => ref _segments[_segmentAcquire]; public SegmentRegistrarRingBuffer() { + _segments = new SegmentRegistrar[s_poolSize]; for (int i = 0; i < _segments.Length; i++) { _segments[i] = new SegmentRegistrar(); + _segments[i]._segments = new NativeParallelHashMap(16, Allocator.Persistent); } } @@ -183,6 +188,7 @@ public void ClearAll() { _segments[i]._numQueries = 0; _segments[i]._segments.Clear(); + _segments[i]._segments.Dispose(); } } } @@ -191,17 +197,17 @@ public void ClearAll() NativeArray _queryResults; float _queryResultsTime = -1f; - Dictionary _resultSegments; + NativeParallelHashMap _resultSegments; NativeArray _queryResultsLast; float _queryResultsTimeLast = -1f; - Dictionary _resultSegmentsLast; + NativeParallelHashMap _resultSegmentsLast; struct ReadbackRequest { public AsyncGPUReadbackRequest _request; public float _dataTimestamp; - public Dictionary _segments; + public NativeParallelHashMap _segments; } List _requests = new List(); @@ -223,7 +229,11 @@ public QueryBase() if (_maxQueryCount != OceanRenderer.Instance._lodDataAnimWaves.Settings.MaxQueryCount) { _maxQueryCount = OceanRenderer.Instance._lodDataAnimWaves.Settings.MaxQueryCount; - _queryPosXZ_minGridSize = new Vector3[_maxQueryCount]; + _queryPosXZ_minGridSize = new NativeArray(_maxQueryCount, Allocator.Persistent); + } + else + { + _queryPosXZ_minGridSize = new NativeArray(MAX_QUERY_COUNT_DEFAULT, Allocator.Persistent); } _computeBufQueries = new ComputeBuffer(_maxQueryCount, 12, ComputeBufferType.Default); @@ -244,39 +254,101 @@ public QueryBase() protected abstract void BindInputsAndOutputs(PropertyWrapperComputeStandalone wrapper, ComputeBuffer resultsBuffer); + /// /// Takes a unique request ID and some world space XZ positions, and computes the displacement vector that lands at this position, /// to a good approximation. The world space height of the water at that position is then SeaLevel + displacement.y. /// - protected bool UpdateQueryPoints(int i_ownerHash, float i_minSpatialLength, Vector3[] queryPoints, Vector3[] queryNormals) + protected bool UpdateQueryPoints(int i_ownerHash, + float i_minSpatialLength, + NativeArray queryPoints, + NativeArray queryNormals, + bool useNormals) { - if (queryPoints.Length + _segmentRegistrarRingBuffer.Current._numQueries > _maxQueryCount) + unsafe { - Debug.LogError($"Crest: Max query count ({_maxQueryCount}) exceeded, increase the max query count in the Animated Waves Settings to support a higher number of queries."); + var args = new UQPArgs() + { + i_ownerHash = i_ownerHash, + i_minSpatialLength = i_minSpatialLength, + queryPoints = queryPoints, + queryNormals = queryNormals, + useNormals = (byte)(useNormals ? 1 : 0), + sqrrbNumQueries = _segmentRegistrarRingBuffer.Current._numQueries, + _maxQueryCount = _maxQueryCount, + sqrrbCurrentSegments = _segmentRegistrarRingBuffer.Current._segments, + _queryPosXZ_minGridSize = _queryPosXZ_minGridSize + }; + var ret = UpdateQueryPoints(UnsafeUtility.AddressOf(ref args)); + + _segmentRegistrarRingBuffer.Current._numQueries = args.sqrrbNumQueries; + _segmentRegistrarRingBuffer.Current._segments = args.sqrrbCurrentSegments; + _queryPosXZ_minGridSize = args._queryPosXZ_minGridSize; + return ret; + } + } + + /* + * The below little dance is required to pass native collections as arguments to burst-compiled static functions + * on the main thread in unity 22.1 and below. It mirrors what the job system does internally for job structs. + * Basically, in 22.1 and below, native collections all have DisposeSentinel objects in them, which + * _technically_ makes them managed objects, and which Burst will therefore not accept as arguments to a + * Burst-entrypoint static function. + * + * However, Burst can compile code *using* native collections just fine, which is how bursted jobs can work. + * We just need to trick it into having access to them, which we do via UnsafeUtility.AsRef. + * + * The only difference between what this does and what the job system does is that the job system sets the + * disposesentinel fields to null when it copies the job struct to the worker threads, whereas we aren't copying + * them anyway, so we just leave them alone. + * + * In 22.2, DisposeSentinel is no longer a class, and so this can likely be revisited. + */ + struct UQPArgs + { + public int i_ownerHash; + public float i_minSpatialLength; + public NativeArray queryPoints; + public NativeArray queryNormals; + public byte useNormals; + public int sqrrbNumQueries; + public int _maxQueryCount; + public NativeParallelHashMap sqrrbCurrentSegments; + public NativeArray _queryPosXZ_minGridSize; + } + + [BurstCompile] + protected static unsafe bool UpdateQueryPoints(void *args) + { + ref var realargs = ref UnsafeUtility.AsRef(args); + + if (realargs.queryPoints.Length + realargs.sqrrbNumQueries > realargs._maxQueryCount) + { + Debug.LogError($"Crest: Max query count ({realargs._maxQueryCount}) exceeded, increase the max query count in the Animated Waves Settings to support a higher number of queries."); return false; } var segmentRetrieved = false; // We'll send in 3 points to get normals - var countPts = (queryPoints != null ? queryPoints.Length : 0); - var countNorms = (queryNormals != null ? queryNormals.Length : 0); + var countPts = realargs.queryPoints.Length; + var countNorms = (realargs.useNormals != 0 ? realargs.queryNormals.Length : 0); var countTotal = countPts + countNorms * 3; - if (_segmentRegistrarRingBuffer.Current._segments.TryGetValue(i_ownerHash, out var segment)) + if (realargs.sqrrbCurrentSegments.TryGetValue(realargs.i_ownerHash, out var segment)) { var segmentSize = segment.y - segment.x + 1; if (segmentSize == countTotal) { // Update frame count segment.z = Time.frameCount; - _segmentRegistrarRingBuffer.Current._segments[i_ownerHash] = segment; + realargs.sqrrbCurrentSegments[realargs.i_ownerHash] = segment; segmentRetrieved = true; } else { - _segmentRegistrarRingBuffer.Current._segments.Remove(i_ownerHash); + realargs.sqrrbCurrentSegments.Remove(realargs.i_ownerHash); } } @@ -288,39 +360,42 @@ protected bool UpdateQueryPoints(int i_ownerHash, float i_minSpatialLength, Vect if (!segmentRetrieved) { - if (_segmentRegistrarRingBuffer.Current._segments.Count >= s_maxGuids) + if (realargs.sqrrbCurrentSegments.Count() >= s_maxGuids) { Debug.LogError("Crest: Too many guids registered with CollProviderCompute. Increase s_maxGuids."); return false; } - segment.x = _segmentRegistrarRingBuffer.Current._numQueries; + segment.x = realargs.sqrrbNumQueries; segment.y = segment.x + countTotal - 1; segment.z = Time.frameCount; - _segmentRegistrarRingBuffer.Current._segments.Add(i_ownerHash, segment); + realargs.sqrrbCurrentSegments.Add(realargs.i_ownerHash, segment); - _segmentRegistrarRingBuffer.Current._numQueries += countTotal; + realargs.sqrrbNumQueries += countTotal; //Debug.Log("Crest: Added points for " + guid); } // The smallest wavelengths should repeat no more than twice across the smaller spatial length. Unless we're // in the last LOD - then this is the best we can do. - float minWavelength = i_minSpatialLength / 2f; + float minWavelength = realargs.i_minSpatialLength / 2f; float samplesPerWave = 2f; float minGridSize = minWavelength / samplesPerWave; - if (countPts + segment.x > _queryPosXZ_minGridSize.Length) + if (countPts + segment.x > realargs._queryPosXZ_minGridSize.Length) { Debug.LogError("Crest: Too many wave height queries. Increase Max Query Count in the Animated Waves Settings."); return false; } + Vector3 tmp; for (int pointi = 0; pointi < countPts; pointi++) { - _queryPosXZ_minGridSize[pointi + segment.x].x = queryPoints[pointi].x; - _queryPosXZ_minGridSize[pointi + segment.x].y = queryPoints[pointi].z; - _queryPosXZ_minGridSize[pointi + segment.x].z = minGridSize; + tmp = realargs._queryPosXZ_minGridSize[pointi + segment.x]; + tmp.x = realargs.queryPoints[pointi].x; + tmp.y = realargs.queryPoints[pointi].z; + tmp.z = minGridSize; + realargs._queryPosXZ_minGridSize[pointi + segment.x] = tmp; } // To compute each normal, post 3 query points @@ -328,17 +403,23 @@ protected bool UpdateQueryPoints(int i_ownerHash, float i_minSpatialLength, Vect { var arrIdx = segment.x + countPts + 3 * normi; - _queryPosXZ_minGridSize[arrIdx + 0].x = queryNormals[normi].x; - _queryPosXZ_minGridSize[arrIdx + 0].y = queryNormals[normi].z; - _queryPosXZ_minGridSize[arrIdx + 0].z = minGridSize; + tmp = realargs._queryPosXZ_minGridSize[arrIdx + 0]; + tmp.x = realargs.queryNormals[normi].x; + tmp.y = realargs.queryNormals[normi].z; + tmp.z = minGridSize; + realargs._queryPosXZ_minGridSize[arrIdx + 0] = tmp; - _queryPosXZ_minGridSize[arrIdx + 1].x = queryNormals[normi].x + s_finiteDiffDx; - _queryPosXZ_minGridSize[arrIdx + 1].y = queryNormals[normi].z; - _queryPosXZ_minGridSize[arrIdx + 1].z = minGridSize; + tmp = realargs._queryPosXZ_minGridSize[arrIdx + 1]; + tmp.x = realargs.queryNormals[normi].x + s_finiteDiffDx; + tmp.y = realargs.queryNormals[normi].z; + tmp.z = minGridSize; + realargs._queryPosXZ_minGridSize[arrIdx + 1] = tmp; - _queryPosXZ_minGridSize[arrIdx + 2].x = queryNormals[normi].x; - _queryPosXZ_minGridSize[arrIdx + 2].y = queryNormals[normi].z + s_finiteDiffDx; - _queryPosXZ_minGridSize[arrIdx + 2].z = minGridSize; + tmp = realargs._queryPosXZ_minGridSize[arrIdx + 2]; + tmp.x = realargs.queryNormals[normi].x; + tmp.y = realargs.queryNormals[normi].z + s_finiteDiffDx; + tmp.z = minGridSize; + realargs._queryPosXZ_minGridSize[arrIdx + 2] = tmp; } return true; @@ -364,9 +445,12 @@ public void CompactQueryStorage() /// /// Copy out displacements, heights, normals. Pass null if info is not required. /// - protected bool RetrieveResults(int guid, Vector3[] displacements, float[] heights, Vector3[] normals) + protected bool RetrieveResults(int guid, + NativeArray displacements, + NativeArray heights, + NativeArray normals) { - if (_resultSegments == null) + if (!_resultSegments.IsCreated) { return false; } @@ -379,18 +463,18 @@ protected bool RetrieveResults(int guid, Vector3[] displacements, float[] height } var countPoints = 0; - if (displacements != null) countPoints = displacements.Length; - if (heights != null) countPoints = heights.Length; - if (displacements != null && heights != null) Debug.Assert(displacements.Length == heights.Length); - var countNorms = (normals != null ? normals.Length : 0); + if (displacements.Length > 0) countPoints = displacements.Length; + if (heights.Length > 0) countPoints = heights.Length; + if (displacements.Length > 0 && heights.Length > 0) Debug.Assert(displacements.Length == heights.Length); + var countNorms = normals.Length; if (countPoints > 0) { // Retrieve Results - if (displacements != null) _queryResults.Slice(segment.x, countPoints).CopyTo(displacements); + if (displacements.Length > 0) _queryResults.Slice(segment.x, countPoints).CopyTo(displacements); // Retrieve Result heights - if (heights != null) + if (heights.Length > 0) { var seaLevel = OceanRenderer.Instance.SeaLevel; for (int i = 0; i < countPoints; i++) @@ -412,8 +496,9 @@ protected bool RetrieveResults(int guid, Vector3[] displacements, float[] height var px = dx + _queryResults[firstNorm + 3 * i + 1]; var pz = dz + _queryResults[firstNorm + 3 * i + 2]; - normals[i] = Vector3.Cross(p - px, p - pz).normalized; - normals[i].y *= -1f; + var tmp = Vector3.Cross(p - px, p - pz).normalized; + tmp.y *= -1f; + normals[i] = tmp; } } @@ -424,20 +509,50 @@ protected bool RetrieveResults(int guid, Vector3[] displacements, float[] height /// Compute time derivative of the displacements by calculating difference from last query. More complicated than it would seem - results /// may not be available in one or both of the results, or the query locations in the array may change. /// - protected int CalculateVelocities(int i_ownerHash, Vector3[] results) + protected unsafe int CalculateVelocities(int i_ownerHash, NativeArray results) { + var args = new CVArgs() + { + i_ownerHash = i_ownerHash, + _queryResultsTime = _queryResultsTime, + _queryResultsTimeLast = _queryResultsTimeLast, + _resultSegments = _resultSegments, + _resultSegmentsLast = _resultSegmentsLast, + results = results, + _queryResults = _queryResults, + _queryResultsLast = _queryResultsLast + }; + return CalculateVelocities(UnsafeUtility.AddressOf(ref args)); + } + + //see comment on UQPArgs as to why we are doing this dance + struct CVArgs + { + public int i_ownerHash; + public float _queryResultsTime; + public float _queryResultsTimeLast; + public NativeParallelHashMap _resultSegments; + public NativeParallelHashMap _resultSegmentsLast; + public NativeArray results; + public NativeArray _queryResults; + public NativeArray _queryResultsLast; + } + [BurstCompile] + protected static unsafe int CalculateVelocities(void* args) + { + ref var realargs = ref UnsafeUtility.AsRef(args); // Need at least 2 returned results to do finite difference - if (_queryResultsTime < 0f || _queryResultsTimeLast < 0f) + if (realargs._queryResultsTime < 0f || realargs._queryResultsTimeLast < 0f) { return 1; } - if (!_resultSegments.TryGetValue(i_ownerHash, out var segment)) + if (!realargs._resultSegments.TryGetValue(realargs.i_ownerHash, out var segment)) { return (int)QueryStatus.RetrieveFailed; } - if (!_resultSegmentsLast.TryGetValue(i_ownerHash, out var segmentLast)) + if (!realargs._resultSegmentsLast.TryGetValue(realargs.i_ownerHash, out var segmentLast)) { return (int)QueryStatus.NotEnoughDataForVels; } @@ -448,16 +563,16 @@ protected int CalculateVelocities(int i_ownerHash, Vector3[] results) return (int)QueryStatus.VelocityDataInvalidated; } - var dt = _queryResultsTime - _queryResultsTimeLast; + var dt = realargs._queryResultsTime - realargs._queryResultsTimeLast; if (dt < 0.0001f) { return (int)QueryStatus.InvalidDtForVelocity; } - var count = results.Length; + var count = realargs.results.Length; for (var i = 0; i < count; i++) { - results[i] = (_queryResults[i + segment.x] - _queryResultsLast[i + segmentLast.x]) / dt; + realargs.results[i] = (realargs._queryResults[i + segment.x] - realargs._queryResultsLast[i + segmentLast.x]) / dt; } return 0; @@ -554,19 +669,27 @@ public void CleanUp() _queryResults.Dispose(); _queryResultsLast.Dispose(); + _queryPosXZ_minGridSize.Dispose(); + _segmentRegistrarRingBuffer.ClearAll(); } - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultDisps, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels, + bool useNormals) { var result = (int)QueryStatus.OK; - if (!UpdateQueryPoints(i_ownerHash, i_minSpatialLength, i_queryPoints, o_resultNorms != null ? i_queryPoints : null)) + if (!UpdateQueryPoints(i_ownerHash, i_minSpatialLength, i_queryPoints, i_queryPoints, useNormals)) { result |= (int)QueryStatus.PostFailed; } - if (!RetrieveResults(i_ownerHash, o_resultDisps, null, o_resultNorms)) + if (!RetrieveResults(i_ownerHash, o_resultDisps, default, o_resultNorms)) { result |= (int)QueryStatus.RetrieveFailed; } @@ -584,7 +707,7 @@ public bool RetrieveSucceeded(int queryStatus) return (queryStatus & (int)QueryStatus.RetrieveFailed) == 0; } - public int ResultGuidCount => _resultSegments != null ? _resultSegments.Count : 0; + public int ResultGuidCount => _resultSegments.Count(); public int RequestCount => _requests != null ? _requests.Count : 0; } diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/QueryDisplacements.cs b/crest/Assets/Crest/Crest/Scripts/Collision/QueryDisplacements.cs index c6af4a99e..a4f9afde3 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/QueryDisplacements.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/QueryDisplacements.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -28,21 +29,27 @@ protected override void BindInputsAndOutputs(PropertyWrapperComputeStandalone wr ShaderProcessQueries.SetBuffer(_kernelHandle, OceanRenderer.sp_cascadeData, OceanRenderer.Instance._bufCascadeDataTgt); } - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultHeights, + ref NativeArray o_resultNorms, + ref NativeArray o_resultVels) { var result = (int)QueryStatus.OK; + var useNormals = o_resultNorms.Length > 0; - if (!UpdateQueryPoints(i_ownerHash, i_minSpatialLength, i_queryPoints, o_resultNorms != null ? i_queryPoints : null)) + if (!UpdateQueryPoints(i_ownerHash, i_minSpatialLength, i_queryPoints, useNormals ? i_queryPoints : default, useNormals)) { result |= (int)QueryStatus.PostFailed; } - if (!RetrieveResults(i_ownerHash, null, o_resultHeights, o_resultNorms)) + if (!RetrieveResults(i_ownerHash, default, o_resultHeights, o_resultNorms)) { result |= (int)QueryStatus.RetrieveFailed; } - if (o_resultVels != null) + if (o_resultVels.Length > 0) { result |= CalculateVelocities(i_ownerHash, o_resultVels); } diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/QueryFlow.cs b/crest/Assets/Crest/Crest/Scripts/Collision/QueryFlow.cs index 921e6009a..01e264106 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/QueryFlow.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/QueryFlow.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -23,9 +24,15 @@ protected override void BindInputsAndOutputs(PropertyWrapperComputeStandalone wr ShaderProcessQueries.SetBuffer(_kernelHandle, sp_ResultFlows, resultsBuffer); } - public int Query(int i_ownerHash, float i_minSpatialLength, Vector3[] i_queryPoints, Vector3[] o_resultFlows) + public int Query(int i_ownerHash, + float i_minSpatialLength, + ref NativeArray i_queryPoints, + ref NativeArray o_resultFlows) { - return Query(i_ownerHash, i_minSpatialLength, i_queryPoints, o_resultFlows, null, null); + var norms = default(NativeArray); + var vels = default(NativeArray); + + return Query(i_ownerHash, i_minSpatialLength, ref i_queryPoints, ref o_resultFlows, ref norms, ref vels, false); } } } diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/RayTraceHelper.cs b/crest/Assets/Crest/Crest/Scripts/Collision/RayTraceHelper.cs index 8161409a5..b9c14df90 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/RayTraceHelper.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/RayTraceHelper.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -12,8 +13,8 @@ namespace Crest /// public class RayTraceHelper { - Vector3[] _queryPos; - Vector3[] _queryResult; + NativeArray _queryPos; + NativeArray _queryResult; float _rayLength; float _rayStepSize; @@ -41,8 +42,17 @@ public RayTraceHelper(float rayLength, float rayStepSize = 2f) Debug.LogWarning($"Crest: RayTraceHelper: ray steps exceed maximum ({maxStepCount}), step size increased to {_rayStepSize} to reduce step count."); } - _queryPos = new Vector3[stepCount]; - _queryResult = new Vector3[stepCount]; + if (_queryPos.IsCreated) + { + _queryPos.Dispose(); + } + _queryPos = new NativeArray(stepCount, Allocator.Persistent); + if (_queryResult.IsCreated) + { + _queryResult.Dispose(); + } + + _queryResult = new NativeArray(stepCount, Allocator.Persistent); } /// @@ -57,12 +67,6 @@ public void Init(Vector3 i_rayOrigin, Vector3 i_rayDirection) _queryPos[i] = i_rayOrigin + i * _rayStepSize * i_rayDirection; } - var rect = new Rect(); - rect.xMin = Mathf.Min(_queryPos[0].x, _queryPos[_queryPos.Length - 1].x); - rect.yMin = Mathf.Min(_queryPos[0].z, _queryPos[_queryPos.Length - 1].z); - rect.xMax = Mathf.Max(_queryPos[0].x, _queryPos[_queryPos.Length - 1].x); - rect.yMax = Mathf.Max(_queryPos[0].z, _queryPos[_queryPos.Length - 1].z); - // Waves go max double along min length. Thats too much - only allow half of a wave per step. _minLength = _rayStepSize * 4f; } @@ -76,7 +80,9 @@ public bool Trace(out float o_distance) { o_distance = -1f; - var status = OceanRenderer.Instance.CollisionProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult, null, null); + var norms = default(NativeArray); + var vels = default(NativeArray); + var status = OceanRenderer.Instance.CollisionProvider.Query(GetHashCode(), _minLength, ref _queryPos, ref _queryResult, ref norms, ref vels, false); if (!OceanRenderer.Instance.CollisionProvider.RetrieveSucceeded(status)) { diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/SamplingHelpers.cs b/crest/Assets/Crest/Crest/Scripts/Collision/SamplingHelpers.cs index 28bba63ea..5d522bd85 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/SamplingHelpers.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/SamplingHelpers.cs @@ -2,7 +2,11 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using System; +using Unity.Burst; +using Unity.Collections; using UnityEngine; +using Object = UnityEngine.Object; namespace Crest { @@ -10,12 +14,25 @@ namespace Crest /// Helper to obtain the ocean surface height at a single location per frame. This is not particularly efficient to sample a single height, /// but is a fairly common case. /// + [BurstCompile] public class SampleHeightHelper { - Vector3[] _queryPos = new Vector3[1]; - Vector3[] _queryResult = new Vector3[1]; - Vector3[] _queryResultNormal = new Vector3[1]; - Vector3[] _queryResultVel = new Vector3[1]; + Vector3 _queryPos; + private Vector3 _queryResult; + private Vector3 _queryResultNormal; + private Vector3 _queryResultVel; + + //these are only ever used on the main thread, and they're filled in when they're used, so we only need one + //global copy of them. if we had one for every sample height helper, we would spend forever creating and + //destroying nativearrays. consider revisiting this when 22.2 is the minspec, because then nativearray becomes + //fully unmanaged. + + //also, obviously revisit if their usage is ever jobified. + static NativeArray _tmpqueryPos; + static NativeArray _tmpqueryResult; + static NativeArray _tmpqueryResultNormal; + static NativeArray _tmpqueryResultVel; + private static bool HaveRegisteredDomainUnload = false; float _minLength = 0f; @@ -33,7 +50,25 @@ public class SampleHeightHelper /// Pass true if calling from FixedUpdate(). This will omit a warning when there on multipled-FixedUpdate frames. public void Init(Vector3 i_queryPos, float i_minLength = 0f, bool allowMultipleCallsPerFrame = false, Object context = null) { - _queryPos[0] = i_queryPos; + _queryPos = i_queryPos; + if (!_tmpqueryPos.IsCreated) + _tmpqueryPos = new NativeArray(1, Allocator.Persistent); + + if (!_tmpqueryResult.IsCreated) + { + _tmpqueryResult = new NativeArray(1, Allocator.Persistent); + } + + if (!_tmpqueryResultNormal.IsCreated) + { + _tmpqueryResultNormal = new NativeArray(1, Allocator.Persistent); + } + + if (!_tmpqueryResultVel.IsCreated) + { + _tmpqueryResultVel = new NativeArray(1, Allocator.Persistent); + } + _minLength = i_minLength; #if UNITY_EDITOR @@ -42,9 +77,26 @@ public void Init(Vector3 i_queryPos, float i_minLength = 0f, bool allowMultipleC Debug.LogWarning($"Crest: SampleHeightHelper.Init() called multiple times in one frame which is not expected. Each SampleHeightHelper object services a single height query per frame. To perform multiple queries, create multiple SampleHeightHelper objects or use the CollProvider.Query() API directly. (_lastFrame = {_lastFrame})", context); } _lastFrame = OceanRenderer.FrameCount; + + //if we do this registration on every init, it wastes lots of time and garbage. but if we never do it, + //we leak memory. so, do it once if we can't tell that we've done it before. + if (!HaveRegisteredDomainUnload) + { + AppDomain.CurrentDomain.DomainUnload -= OnDomainUnload; + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + HaveRegisteredDomainUnload = true; + } #endif } + private static void OnDomainUnload(object sender, EventArgs e) + { + _tmpqueryPos.Dispose(); + _tmpqueryResult.Dispose(); + _tmpqueryResultNormal.Dispose(); + _tmpqueryResultVel.Dispose(); + } + /// /// Call this to do the query. Can be called only once after Init(). /// @@ -57,15 +109,36 @@ public bool Sample(out float o_height) return false; } - var status = collProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult, null, null); + if (!DoQueryAndRetrieveSucceeded(out o_height, collProvider, _minLength, _queryPos, out var queryResult, GetHashCode())) return false; + + return true; + } + + + private static bool DoQueryAndRetrieveSucceeded(out float o_height, ICollProvider collProvider, float minLength, Vector3 queryPos, out Vector3 queryResult, int selfHashCode) + { + var oResultNorms = new NativeArray(); + var oResultVels = new NativeArray(); + _tmpqueryPos[0] = queryPos; + + var status = collProvider.Query(selfHashCode, + minLength, + ref _tmpqueryPos, + ref _tmpqueryResult, + ref oResultNorms, + ref oResultVels, + false); if (!collProvider.RetrieveSucceeded(status)) { o_height = OceanRenderer.Instance.SeaLevel; + queryResult = _tmpqueryResult[0]; return false; } - o_height = _queryResult[0].y + OceanRenderer.Instance.SeaLevel; + queryResult = _tmpqueryResult[0]; + + o_height = queryResult.y + OceanRenderer.Instance.SeaLevel; return true; } @@ -80,7 +153,17 @@ public bool Sample(out float o_height, out Vector3 o_normal) return false; } - var status = collProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult, _queryResultNormal, null); + NativeArray oResultVels = new NativeArray(); + _tmpqueryPos[0] = _queryPos; + var status = collProvider.Query(GetHashCode(), + _minLength, + ref _tmpqueryPos, + ref _tmpqueryResult, + ref _tmpqueryResultNormal, + ref oResultVels, + true); + _queryResult = _tmpqueryResult[0]; + _queryResultNormal = _tmpqueryResultNormal[0]; if (!collProvider.RetrieveSucceeded(status)) { @@ -89,8 +172,8 @@ public bool Sample(out float o_height, out Vector3 o_normal) return false; } - o_height = _queryResult[0].y + OceanRenderer.Instance.SeaLevel; - o_normal = _queryResultNormal[0]; + o_height = _queryResult.y + OceanRenderer.Instance.SeaLevel; + o_normal = _queryResultNormal; return true; } @@ -106,7 +189,19 @@ public bool Sample(out float o_height, out Vector3 o_normal, out Vector3 o_surfa return false; } - var status = collProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult, _queryResultNormal, _queryResultVel); + _tmpqueryPos[0] = _queryPos; + + var status = collProvider.Query(GetHashCode(), + _minLength, + ref _tmpqueryPos, + ref _tmpqueryResult, + ref _tmpqueryResultNormal, + ref _tmpqueryResultVel, + true); + + _queryResult = _tmpqueryResult[0]; + _queryResultNormal = _tmpqueryResultNormal[0]; + _queryResultVel = _tmpqueryResultVel[0]; if (!collProvider.RetrieveSucceeded(status)) { @@ -116,9 +211,9 @@ public bool Sample(out float o_height, out Vector3 o_normal, out Vector3 o_surfa return false; } - o_height = _queryResult[0].y + OceanRenderer.Instance.SeaLevel; - o_normal = _queryResultNormal[0]; - o_surfaceVel = _queryResultVel[0]; + o_height = _queryResult.y + OceanRenderer.Instance.SeaLevel; + o_normal = _queryResultNormal; + o_surfaceVel = _queryResultVel; return true; } @@ -133,7 +228,18 @@ public bool Sample(out Vector3 o_displacementToPoint, out Vector3 o_normal, out o_surfaceVel = Vector3.zero; return false; } - var status = collProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult, _queryResultNormal, _queryResultVel); + + _tmpqueryPos[0] = _queryPos; + var status = collProvider.Query(GetHashCode(), + _minLength, + ref _tmpqueryPos, + ref _tmpqueryResult, + ref _tmpqueryResultNormal, + ref _tmpqueryResultVel, + true); + _queryResult = _tmpqueryResult[0]; + _queryResultNormal = _tmpqueryResultNormal[0]; + _queryResultVel = _tmpqueryResultVel[0]; if (!collProvider.RetrieveSucceeded(status)) { @@ -143,9 +249,9 @@ public bool Sample(out Vector3 o_displacementToPoint, out Vector3 o_normal, out return false; } - o_displacementToPoint = _queryResult[0]; - o_normal = _queryResultNormal[0]; - o_surfaceVel = _queryResultVel[0]; + o_displacementToPoint = _queryResult; + o_normal = _queryResultNormal; + o_surfaceVel = _queryResultVel; return true; } @@ -157,8 +263,12 @@ public bool Sample(out Vector3 o_displacementToPoint, out Vector3 o_normal, out /// public class SampleFlowHelper { - Vector3[] _queryPos = new Vector3[1]; - Vector3[] _queryResult = new Vector3[1]; + private Vector3 _queryPos; + Vector3 _queryResult; + + //see comment on SampleHeightHelper about why these are static and more + private static NativeArray _tmpQueryPos; + private static NativeArray _tmpQueryResult; float _minLength = 0f; @@ -170,7 +280,16 @@ public class SampleFlowHelper /// pass in the boats width. Larger objects will filter out detailed flow information. public void Init(Vector3 i_queryPos, float i_minLength) { - _queryPos[0] = i_queryPos; + _queryPos = i_queryPos; + if (!_tmpQueryPos.IsCreated) + { + _tmpQueryPos = new NativeArray(1, Allocator.Persistent); + } + + if (!_tmpQueryResult.IsCreated) + { + _tmpQueryResult = new NativeArray(1, Allocator.Persistent); + } _minLength = i_minLength; } @@ -185,8 +304,9 @@ public bool Sample(out Vector2 o_flow) o_flow = Vector2.zero; return false; } - var status = flowProvider.Query(GetHashCode(), _minLength, _queryPos, _queryResult); - + _tmpQueryPos[0] = _queryPos; + var status = flowProvider.Query(GetHashCode(), _minLength, ref _tmpQueryPos, ref _tmpQueryResult); + _queryResult = _tmpQueryResult[0]; if (!flowProvider.RetrieveSucceeded(status)) { o_flow = Vector2.zero; @@ -194,8 +314,8 @@ public bool Sample(out Vector2 o_flow) } // We don't support float2 queries unfortunately, so unpack from float3 - o_flow.x = _queryResult[0].x; - o_flow.y = _queryResult[0].z; + o_flow.x = _queryResult.x; + o_flow.y = _queryResult.z; return true; } diff --git a/crest/Assets/Crest/Crest/Scripts/Collision/VisualiseCollisionArea.cs b/crest/Assets/Crest/Crest/Scripts/Collision/VisualiseCollisionArea.cs index ee51ada20..90c492b5c 100644 --- a/crest/Assets/Crest/Crest/Scripts/Collision/VisualiseCollisionArea.cs +++ b/crest/Assets/Crest/Crest/Scripts/Collision/VisualiseCollisionArea.cs @@ -2,6 +2,7 @@ // This file is subject to the MIT License as seen in the root of this folder structure (LICENSE) +using Unity.Collections; using UnityEngine; namespace Crest @@ -37,11 +38,11 @@ public class VisualiseCollisionArea : MonoBehaviour [SerializeField] bool _useNormals; - float[] _resultHeights; - Vector3[] _resultDisps; - Vector3[] _resultNorms; + NativeArray _resultHeights; + NativeArray _resultDisps; + NativeArray _resultNorms; - Vector3[] _samplePositions; + NativeArray _samplePositions; void Update() { @@ -52,15 +53,15 @@ void Update() if (_resultHeights == null || _resultHeights.Length != _steps * _steps) { - _resultHeights = new float[_steps * _steps]; + _resultHeights = new NativeArray(_steps * _steps, Allocator.Persistent); } if (_resultDisps == null || _resultDisps.Length != _steps * _steps) { - _resultDisps = new Vector3[_steps * _steps]; + _resultDisps = new NativeArray(_steps * _steps, Allocator.Persistent); } if (_resultNorms == null || _resultNorms.Length != _steps * _steps) { - _resultNorms = new Vector3[_steps * _steps]; + _resultNorms = new NativeArray(_steps * _steps, Allocator.Persistent); for (int i = 0; i < _resultNorms.Length; i++) { @@ -69,7 +70,7 @@ void Update() } if (_samplePositions == null || _samplePositions.Length != _steps * _steps) { - _samplePositions = new Vector3[_steps * _steps]; + _samplePositions = new NativeArray(_steps * _steps, Allocator.Persistent); } var collProvider = OceanRenderer.Instance.CollisionProvider; @@ -78,16 +79,17 @@ void Update() { for (int j = 0; j < _steps; j++) { - _samplePositions[j * _steps + i] = new Vector3(((i + 0.5f) - _steps / 2f) * _stepSize, 0f, ((j + 0.5f) - _steps / 2f) * _stepSize); - _samplePositions[j * _steps + i].x += transform.position.x; - _samplePositions[j * _steps + i].z += transform.position.z; + var tmp = new Vector3(((i + 0.5f) - _steps / 2f) * _stepSize, 0f, ((j + 0.5f) - _steps / 2f) * _stepSize); + tmp.x += transform.position.x; + tmp.z += transform.position.z; + _samplePositions[j * _steps + i] = tmp; } } if (_useDisplacements) { - if (collProvider.RetrieveSucceeded(collProvider.Query(GetHashCode(), _objectWidth, _samplePositions, _resultDisps, _useNormals ? _resultNorms : null, null))) - { + var oResultVels = new NativeArray(); + if (collProvider.RetrieveSucceeded(collProvider.Query(GetHashCode(), _objectWidth, ref _samplePositions, ref _resultDisps, ref _resultNorms, ref oResultVels, _useNormals))) { for (int i = 0; i < _steps; i++) { for (int j = 0; j < _steps; j++) @@ -105,8 +107,8 @@ void Update() } else { - if (collProvider.RetrieveSucceeded(collProvider.Query(GetHashCode(), _objectWidth, _samplePositions, _resultHeights, _useNormals ? _resultNorms : null, null))) - { + var oResultVels = new NativeArray(); + if (collProvider.RetrieveSucceeded(collProvider.Query(GetHashCode(), _objectWidth, ref _samplePositions, ref _resultHeights, ref _resultNorms, ref oResultVels))) { for (int i = 0; i < _steps; i++) { for (int j = 0; j < _steps; j++) diff --git a/crest/Assets/Crest/Crest/Scripts/Crest.asmdef b/crest/Assets/Crest/Crest/Scripts/Crest.asmdef index 17ffb0703..a259f4577 100644 --- a/crest/Assets/Crest/Crest/Scripts/Crest.asmdef +++ b/crest/Assets/Crest/Crest/Scripts/Crest.asmdef @@ -5,11 +5,12 @@ "Unity.Postprocessing.Runtime", "Unity.InputSystem", "Unity.Mathematics", - "Unity.Burst" + "Unity.Burst", + "Unity.Collections" ], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/crest/Assets/Crest/Crest/Scripts/Interaction/BoatProbes.cs b/crest/Assets/Crest/Crest/Scripts/Interaction/BoatProbes.cs index bf187f628..c82da428f 100644 --- a/crest/Assets/Crest/Crest/Scripts/Interaction/BoatProbes.cs +++ b/crest/Assets/Crest/Crest/Scripts/Interaction/BoatProbes.cs @@ -5,6 +5,7 @@ // Shout out to @holdingjason who posted a first version of this script here: https://github.com/huwb/crest-oceanrender/pull/100 using System; +using Unity.Collections; using UnityEngine; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; @@ -83,9 +84,9 @@ class DebugFields float _totalWeight; - Vector3[] _queryPoints; - Vector3[] _queryResultDisps; - Vector3[] _queryResultVels; + NativeArray _queryPoints; + NativeArray _queryResultDisps; + NativeArray _queryResultVels; SampleFlowHelper _sampleFlowHelper = new SampleFlowHelper(); @@ -102,9 +103,9 @@ private void Start() CalcTotalWeight(); - _queryPoints = new Vector3[_forcePoints.Length + 1]; - _queryResultDisps = new Vector3[_forcePoints.Length + 1]; - _queryResultVels = new Vector3[_forcePoints.Length + 1]; + _queryPoints = new NativeArray(_forcePoints.Length + 1, Allocator.Persistent); + _queryResultDisps = new NativeArray(_forcePoints.Length + 1, Allocator.Persistent); + _queryResultVels = new NativeArray(_forcePoints.Length + 1, Allocator.Persistent); } void CalcTotalWeight() @@ -165,7 +166,8 @@ void UpdateWaterQueries(ICollProvider collProvider) } _queryPoints[_forcePoints.Length] = transform.position; - collProvider.Query(GetHashCode(), ObjectWidth, _queryPoints, _queryResultDisps, null, _queryResultVels); + var oResultNorms = new NativeArray(); + collProvider.Query(GetHashCode(), ObjectWidth, ref _queryPoints, ref _queryResultDisps, ref oResultNorms, ref _queryResultVels, false); if (_debug._drawQueries) { diff --git a/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgr.cs b/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgr.cs index d0131ca6a..1e123036c 100644 --- a/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgr.cs +++ b/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgr.cs @@ -223,12 +223,13 @@ protected void SubmitDraws(int lodIdx, CommandBuffer buf) var drawList = RegisterLodDataInputBase.GetRegistrar(GetType()); foreach (var draw in drawList) { - if (!draw.Value.Enabled) + var value = draw.Value; + if (!value.Enabled) { continue; } - draw.Value.Draw(this, buf, 1f, 0, lodIdx); + value.Draw(this, buf, 1f, 0, lodIdx); } } diff --git a/crest/Packages/manifest.json b/crest/Packages/manifest.json index 9cf6112c3..bfab4133f 100644 --- a/crest/Packages/manifest.json +++ b/crest/Packages/manifest.json @@ -4,6 +4,7 @@ "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", "com.unity.burst": "1.6.5", + "com.unity.collections": "1.3.1", "com.unity.ide.rider": "3.0.14", "com.unity.ide.visualstudio": "2.0.15", "com.unity.ide.vscode": "1.2.5",