diff --git a/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneBufferCombiner.cs b/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneBufferCombiner.cs index cc4a9da2d..f22112b5e 100644 --- a/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneBufferCombiner.cs +++ b/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneBufferCombiner.cs @@ -4,6 +4,8 @@ using Unity.Jobs; using UnityEngine.Profiling; using UniGLTF.SpringBoneJobs.InputPorts; using UnityEngine; +using Unity.Collections; +using System.Linq; namespace UniGLTF.SpringBoneJobs { @@ -20,19 +22,17 @@ namespace UniGLTF.SpringBoneJobs private FastSpringBoneCombinedBuffer _combinedBuffer; public FastSpringBoneCombinedBuffer Combined => _combinedBuffer; private readonly LinkedList _buffers = new LinkedList(); - private bool _isDirty; + private Queue<(bool isAdd, FastSpringBoneBuffer buffer)> _request = new(); public bool HasBuffer => _buffers.Count > 0 && _combinedBuffer != null; public void Register(FastSpringBoneBuffer buffer) { - _buffers.AddLast(buffer); - _isDirty = true; + _request.Enqueue((true, buffer)); } public void Unregister(FastSpringBoneBuffer buffer) { - _buffers.Remove(buffer); - _isDirty = true; + _request.Enqueue((false, buffer)); } /// @@ -40,14 +40,45 @@ namespace UniGLTF.SpringBoneJobs /// public JobHandle ReconstructIfDirty(JobHandle handle) { - if (_isDirty) + if (_request.Count == 0) { - var result = ReconstructBuffers(handle); - _isDirty = false; - return result; + return handle; } - return handle; + if (_combinedBuffer is FastSpringBoneCombinedBuffer combined) + { + // index が変わる前に シミュレーションの状態を保存する。 + // 状態の保存場所が BlittableJoint から CurrentTails に移動しているのでここでやる。 + var logicsIndex = 0; + foreach (var buffer in _buffers) + { + if (_request.Any(x => !x.isAdd && x.buffer == buffer)) + { + // 削除するので skip + continue; + } + buffer.BackupCurrentTails(combined.CurrentTails, combined.NextTails, logicsIndex); + logicsIndex += buffer.Logics.Length; + } + } + + // buffer 増減 + while (_request.Count > 0) + { + var (isAdd, buffer) = _request.Dequeue(); + if (isAdd) + { + // 速度 0 にする + _buffers.AddLast(buffer); + } + else + { + _buffers.Remove(buffer); + } + } + + // 再構築 + return ReconstructBuffers(handle); } /// @@ -60,10 +91,6 @@ namespace UniGLTF.SpringBoneJobs Profiler.BeginSample("FastSpringBone.ReconstructBuffers.DisposeBuffers"); if (_combinedBuffer is FastSpringBoneCombinedBuffer combined) { - Profiler.BeginSample("FastSpringBone.ReconstructBuffers.SaveToSourceBuffer"); - combined.SaveToSourceBuffer(); - Profiler.EndSample(); - // TODO: Dispose せずに再利用? combined.Dispose(); } diff --git a/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneConbinedBuffer.cs b/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneConbinedBuffer.cs index d6bd83c05..f06f3f70a 100644 --- a/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneConbinedBuffer.cs +++ b/Assets/UniGLTF/Runtime/SpringBoneJobs/FastSpringBoneConbinedBuffer.cs @@ -97,7 +97,7 @@ namespace UniGLTF.SpringBoneJobs springsCount += buffer.Springs.Length; collidersCount += buffer.Colliders.Length; logicsCount += buffer.Logics.Length; - transformsCount += buffer.BlittableTransforms.Length; + transformsCount += buffer.Transforms.Length; } Profiler.EndSample(); @@ -112,6 +112,24 @@ namespace UniGLTF.SpringBoneJobs private JobHandle Batching(JobHandle handle) { + // TransformAccessArrayの構築 + Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray"); + var transforms = new Transform[_transforms.Length]; + var transformAccessArrayOffset = 0; + foreach (var buffer in _batchedBuffers) + { + Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length); + transformAccessArrayOffset += buffer.Transforms.Length; + } + _transformAccessArray = new TransformAccessArray(transforms); + Profiler.EndSample(); + + // Transforms を更新。後続の InitCurrentTails で使う + handle = new PullTransformJob + { + Transforms = Transforms + }.Schedule(TransformAccessArray, handle); + Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs"); var springsOffset = 0; var collidersOffset = 0; @@ -129,13 +147,8 @@ namespace UniGLTF.SpringBoneJobs _jointMap.Add(buffer.Transforms[head], logicsOffset + j); } - // バッファの読み込みをスケジュール - handle = new LoadTransformsJob - { - SrcTransforms = buffer.BlittableTransforms, - DestTransforms = new NativeSlice(_transforms, transformOffset, - buffer.BlittableTransforms.Length) - }.Schedule(buffer.BlittableTransforms.Length, 1, handle); + // 速度の維持 + buffer.RestoreCurrentTails(_currentTails, _nextTails, logicsOffset); handle = new LoadSpringsJob { @@ -165,26 +178,10 @@ namespace UniGLTF.SpringBoneJobs springsOffset += buffer.Springs.Length; collidersOffset += buffer.Colliders.Length; logicsOffset += buffer.Logics.Length; - transformOffset += buffer.BlittableTransforms.Length; + transformOffset += buffer.Transforms.Length; } handle = InitCurrentTails(handle); - - // TransformAccessArrayの構築と並行してJobを行うため、この時点で走らせておく - JobHandle.ScheduleBatchedJobs(); - Profiler.EndSample(); - - // TransformAccessArrayの構築 - Profiler.BeginSample("FastSpringBone.ReconstructBuffers.LoadTransformAccessArray"); - var transforms = new Transform[_transforms.Length]; - var transformAccessArrayOffset = 0; - foreach (var buffer in _batchedBuffers) - { - Array.Copy(buffer.Transforms, 0, transforms, transformAccessArrayOffset, buffer.Transforms.Length); - transformAccessArrayOffset += buffer.BlittableTransforms.Length; - } - - _transformAccessArray = new TransformAccessArray(transforms); Profiler.EndSample(); return handle; @@ -208,25 +205,6 @@ namespace UniGLTF.SpringBoneJobs if (_transformAccessArray.isCreated) _transformAccessArray.Dispose(); } - /// - /// バッチングされたバッファから、個々のバッファへと値を戻す - /// Logics to _batchedBuffers[].Logics - /// バッファの再構築前にこの処理を行わないと、揺れの状態がリセットされてしまい、不自然な挙動になる - /// - internal void SaveToSourceBuffer() - { - var logicsIndex = 0; - for (var i = 0; i < _batchedBuffers.Length; ++i) - { - var length = _batchedBufferLogicSizes[i]; - if (!_batchedBuffers[i].IsDisposed && length > 0) - { - NativeArray.Copy(Logics, logicsIndex, _batchedBuffers[i].Logics, 0, length); - } - logicsIndex += length; - } - } - public void FlipBuffer() { var tmp = _prevTails; @@ -251,23 +229,6 @@ namespace UniGLTF.SpringBoneJobs } } -#if ENABLE_SPRINGBONE_BURST - [BurstCompile] -#endif - /// - /// - /// - private struct LoadTransformsJob : IJobParallelFor - { - [ReadOnly] public NativeArray SrcTransforms; - [WriteOnly] public NativeSlice DestTransforms; - - public void Execute(int index) - { - DestTransforms[index] = SrcTransforms[index]; - } - } - #if ENABLE_SPRINGBONE_BURST [BurstCompile] #endif @@ -328,49 +289,69 @@ namespace UniGLTF.SpringBoneJobs #endif private struct InitCurrentTailsJob : IJobParallelFor { + [ReadOnly] public NativeArray Springs; + [ReadOnly] public NativeArray Logics; [ReadOnly] public NativeArray Transforms; - [WriteOnly] public NativeSlice CurrentTails; - [WriteOnly] public NativeSlice PrevTails; - [WriteOnly] public NativeSlice NextTails; + [NativeDisableParallelForRestriction] public NativeSlice CurrentTails; + [NativeDisableParallelForRestriction] public NativeSlice PrevTails; + [NativeDisableParallelForRestriction] public NativeSlice NextTails; - public void Execute(int jointIndex) + public void Execute(int springIndex) { - var tailIndex = Logics[jointIndex].tailTransformIndex; - if (tailIndex == -1) + var spring = Springs[springIndex]; + for (int jointIndex = spring.logicSpan.startIndex; jointIndex < spring.logicSpan.EndIndex; ++jointIndex) { - // tail 無い - var tail = Transforms[Logics[jointIndex].headTransformIndex]; - CurrentTails[jointIndex] = tail.position; - PrevTails[jointIndex] = tail.position; - NextTails[jointIndex] = tail.position; - } - else - { - var tail = Transforms[tailIndex]; - CurrentTails[jointIndex] = tail.position; - PrevTails[jointIndex] = tail.position; - NextTails[jointIndex] = tail.position; + if (float.IsNaN(CurrentTails[jointIndex].x)) + { + // Transsform の現状を使う。velocity を zero にする + int tailIndex; + if (Logics[jointIndex].tailTransformIndex == -1) + { + // tail 無い + tailIndex = spring.transformIndexOffset + Logics[jointIndex].headTransformIndex; + } + else + { + tailIndex = spring.transformIndexOffset + Logics[jointIndex].tailTransformIndex; + } + + var tail = Transforms[tailIndex]; + CurrentTails[jointIndex] = tail.position; + PrevTails[jointIndex] = tail.position; + NextTails[jointIndex] = tail.position; + } } } } - /// - /// Transform から currentTail を更新。 - /// prevTail も同じ内容にする(速度0)。 + /// + /// # CurrentTails[i] == NAN + /// + /// Transforms から Current, Prev, Next を代入する。 + /// 速度 0 で初期化することになる。 + /// + /// # CurrentTails[i] != NAN + /// + /// 本処理はスキップされて Current, Next の利用が継続されます。 + /// + /// # NAN + /// + /// Batching 関数内の FastSpringBoneBuffer.RestoreCurrentTails にて backup の Current, Next が無かったときに + /// 目印として NAN が代入されます。 /// - /// - /// public JobHandle InitCurrentTails(JobHandle handle) { return new InitCurrentTailsJob { + Springs = Springs, + Logics = Logics, Transforms = Transforms, CurrentTails = CurrentTails, PrevTails = PrevTails, NextTails = NextTails, - }.Schedule(Logics.Length, 1, handle); + }.Schedule(Springs.Length, 1, handle); } public void InitializeJointsLocalRotation(FastSpringBoneBuffer buffer) @@ -379,9 +360,9 @@ namespace UniGLTF.SpringBoneJobs for (var i = 0; i < _batchedBuffers.Length; ++i) { var length = _batchedBufferLogicSizes[i]; - Debug.Assert(length == buffer.Logics.Length); if (_batchedBuffers[i] == buffer) { + Debug.Assert(length == buffer.Logics.Length); for (var j = 0; j < length; ++j) { var logic = buffer.Logics[j]; @@ -401,22 +382,23 @@ namespace UniGLTF.SpringBoneJobs public void DrawGizmos() { - foreach (var collider in _colliders) - { - collider.DrawGizmo(_transforms[collider.transformIndex]); - } - foreach (var spring in _springs) { + for (int i = spring.colliderSpan.startIndex; i < spring.colliderSpan.EndIndex; ++i) + { + var collider = _colliders[i]; + collider.DrawGizmo(_transforms[spring.transformIndexOffset + collider.transformIndex]); + } + for (int i = spring.logicSpan.startIndex; i < spring.logicSpan.EndIndex; ++i) { var joint = _logics[i]; - joint.DrawGizmo(_transforms[joint.tailTransformIndex], _joints[i]); + joint.DrawGizmo(_transforms[spring.transformIndexOffset + joint.tailTransformIndex], _joints[i]); Gizmos.matrix = Matrix4x4.identity; Gizmos.DrawLine( - _transforms[joint.tailTransformIndex].position, - _transforms[joint.headTransformIndex].position); + _transforms[spring.transformIndexOffset + joint.tailTransformIndex].position, + _transforms[spring.transformIndexOffset + joint.headTransformIndex].position); } } } diff --git a/Assets/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs b/Assets/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs index 9e9887176..455e17dd6 100644 --- a/Assets/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs +++ b/Assets/UniGLTF/Runtime/SpringBoneJobs/InputPorts/FastSpringBoneBuffer.cs @@ -22,9 +22,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts public NativeArray Joints { get; } public NativeArray Colliders { get; } public NativeArray Logics { get; } - public NativeArray BlittableTransforms { get; } + private NativeArray _currentTailsBackup; + private NativeArray _nextTailsBackup; public Transform[] Transforms { get; } - public bool IsDisposed { get; private set; } /// /// Joint, Collider, Center の Transform のリスト @@ -103,16 +103,6 @@ namespace UniGLTF.SpringBoneJobs.InputPorts Joints = new NativeArray(blittableJoints.ToArray(), Allocator.Persistent); Colliders = new NativeArray(blittableColliders.ToArray(), Allocator.Persistent); Logics = new NativeArray(blittableLogics.ToArray(), Allocator.Persistent); - BlittableTransforms = new NativeArray(Transforms.Select(transform => new BlittableTransform - { - position = transform.position, - rotation = transform.rotation, - localPosition = transform.localPosition, - localRotation = transform.localRotation, - localScale = transform.localScale, - localToWorldMatrix = transform.localToWorldMatrix, - worldToLocalMatrix = transform.worldToLocalMatrix - }).ToArray(), Allocator.Persistent); Profiler.EndSample(); } @@ -151,15 +141,50 @@ namespace UniGLTF.SpringBoneJobs.InputPorts } } + public void BackupCurrentTails(NativeArray currentTails, NativeArray nextTails, int offset) + { + if (!Logics.IsCreated || Logics.Length == 0) + { + return; + } + if (!_currentTailsBackup.IsCreated) + { + _currentTailsBackup = new(Logics.Length, Allocator.Persistent); + } + if (!_nextTailsBackup.IsCreated) + { + _nextTailsBackup = new(Logics.Length, Allocator.Persistent); + } + NativeArray.Copy(currentTails, offset, _currentTailsBackup, 0, Logics.Length); + NativeArray.Copy(nextTails, offset, _nextTailsBackup, 0, Logics.Length); + } + + public void RestoreCurrentTails(NativeArray currentTails, NativeArray nextTails, int offset) + { + if (_currentTailsBackup.IsCreated) + { + NativeArray.Copy(_currentTailsBackup, 0, currentTails, offset, Logics.Length); + NativeArray.Copy(_nextTailsBackup, 0, nextTails, offset, Logics.Length); + } + else + { + var end = offset + Logics.Length; + for (int i = offset; i < end; ++i) + { + // mark velocity zero + currentTails.GetSubArray(offset, Logics.Length).AsSpan().Fill(new Vector3(float.NaN, float.NaN, float.NaN)); + } + } + } + public void Dispose() { - if (IsDisposed) return; - IsDisposed = true; - Springs.Dispose(); - Joints.Dispose(); - BlittableTransforms.Dispose(); - Colliders.Dispose(); - Logics.Dispose(); + if (Springs.IsCreated) Springs.Dispose(); + if (Joints.IsCreated) Joints.Dispose(); + if (Colliders.IsCreated) Colliders.Dispose(); + if (Logics.IsCreated) Logics.Dispose(); + if (_currentTailsBackup.IsCreated) _currentTailsBackup.Dispose(); + if (_nextTailsBackup.IsCreated) _nextTailsBackup.Dispose(); } } } \ No newline at end of file diff --git a/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs index a6a03f23c..c84f58de0 100644 --- a/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs +++ b/Assets/VRM10/Runtime/Components/Vrm10Runtime/FastSpringBoneBufferFactory.cs @@ -26,6 +26,7 @@ namespace UniVRM10 if (fastSpringBoneBuffer != null) { fastSpringBoneBuffer.Dispose(); + fastSpringBoneBuffer = null; } Func GetOrAddDefaultTransformState = (Transform tf) => @@ -41,7 +42,7 @@ namespace UniVRM10 // create(Spring情報の再収集。設定変更の反映) var springs = vrm.SpringBone.Springs.Select(spring => new FastSpringBoneSpring - { + { center = spring.Center, colliders = spring.ColliderGroups .SelectMany(group => group.Colliders)