Merge pull request #2433 from ousttrue/fix/springbone_job_params

[SpringBone] BlittableLogic を immutable にするべく currentTail と prevTail を外出し
This commit is contained in:
ousttrue
2024-09-18 16:08:49 +09:00
committed by GitHub
12 changed files with 180 additions and 107 deletions

View File

@@ -1,18 +0,0 @@
using System;
using UnityEngine;
namespace UniGLTF.SpringBoneJobs.Blittables
{
/// <summary>
/// SpringBoneの各関節を表すデータ型
/// </summary>
[Serializable]
public struct BlittableJoint
{
public float stiffnessForce;
public float gravityPower;
public Vector3 gravityDir;
public float dragForce;
public float radius;
}
}

View File

@@ -4,16 +4,17 @@ using UnityEngine;
namespace UniGLTF.SpringBoneJobs.Blittables
{
/// <summary>
/// SpringBoneの各関節に紐付いた計算情報を表すデータ型
/// Reconstruct に対して Immutable。
/// Jointの増減、初期姿勢の変更など構成の変更は Reconstruct が必要。
/// 変わりにくいスコープ。
/// </summary>
[Serializable]
public struct BlittableLogic
public struct BlittableJointImmutable
{
public int parentTransformIndex;
public int headTransformIndex;
public int tailTransformIndex;
public float length;
public Vector3 currentTail;
public Vector3 prevTail;
public Quaternion localRotation;
public Vector3 boneAxis;
}

View File

@@ -0,0 +1,21 @@
using System;
using UnityEngine;
namespace UniGLTF.SpringBoneJobs.Blittables
{
/// <summary>
/// Reconstruct に対して Mutable。
/// Reconstruct より軽量な JointReconfigure(仮) で変更できるようにする予定。
/// おもに Editor play で設定を変更しながら動作を見る用途を想定している。
/// JointReconfigure を呼ばなければ以前と同じで不変となる。
/// </summary>
[Serializable]
public struct BlittableJointMutable
{
public float stiffnessForce;
public float gravityPower;
public Vector3 gravityDir;
public float dragForce;
public float radius;
}
}

View File

@@ -19,11 +19,19 @@ namespace UniGLTF.SpringBoneJobs
/// </summary>
public sealed class FastSpringBoneBufferCombiner : IDisposable
{
// 長さと index 同じ
private NativeArray<BlittableJointImmutable> _logics;
private NativeArray<BlittableJointMutable> _joints;
private NativeArray<Vector3> _prevTails;
private NativeArray<Vector3> _currentTails;
private NativeArray<Vector3> _nextTails;
private NativeArray<BlittableSpring> _springs;
private NativeArray<BlittableTransform> _transforms;
private NativeArray<BlittableCollider> _colliders;
private NativeArray<BlittableJoint> _joints;
private NativeArray<BlittableLogic> _logics;
private NativeArray<BlittableTransform> _transforms;
private TransformAccessArray _transformAccessArray;
private readonly LinkedList<FastSpringBoneBuffer> _buffers = new LinkedList<FastSpringBoneBuffer>();
@@ -32,15 +40,29 @@ namespace UniGLTF.SpringBoneJobs
private bool _isDirty;
public NativeArray<BlittableJointImmutable> Logics => _logics;
public NativeArray<BlittableJointMutable> Joints => _joints;
public NativeArray<Vector3> PrevTails => _prevTails;
public NativeArray<Vector3> CurrentTails => _currentTails;
public NativeArray<Vector3> NextTails => _nextTails;
public NativeArray<BlittableSpring> Springs => _springs;
public NativeArray<BlittableJoint> Joints => _joints;
public NativeArray<BlittableCollider> Colliders => _colliders;
public NativeArray<BlittableTransform> Transforms => _transforms;
public TransformAccessArray TransformAccessArray => _transformAccessArray;
public NativeArray<BlittableCollider> Colliders => _colliders;
public NativeArray<BlittableLogic> Logics => _logics;
public bool HasBuffer => _batchedBuffers != null && _batchedBuffers.Length > 0;
public void FlipBuffer()
{
var tmp = _prevTails;
_prevTails = _currentTails;
_currentTails = _nextTails;
_nextTails = tmp;
}
public void Register(FastSpringBoneBuffer buffer)
{
_buffers.AddLast(buffer);
@@ -82,7 +104,7 @@ namespace UniGLTF.SpringBoneJobs
var length = _batchedBufferLogicSizes[i];
if (!_batchedBuffers[i].IsDisposed && length > 0)
{
NativeArray<BlittableLogic>.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
NativeArray<BlittableJointImmutable>.Copy(_logics, logicsIndex, _batchedBuffers[i].Logics, 0, length);
}
logicsIndex += length;
@@ -123,26 +145,29 @@ namespace UniGLTF.SpringBoneJobs
logicsCount += buffer.Logics.Length;
transformsCount += buffer.BlittableTransforms.Length;
}
Profiler.EndSample();
// バッファの構築
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.CreateBuffers");
_logics = new NativeArray<BlittableJointImmutable>(logicsCount, Allocator.Persistent);
_joints = new NativeArray<BlittableJointMutable>(logicsCount, Allocator.Persistent);
_prevTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_currentTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_nextTails = new NativeArray<Vector3>(logicsCount, Allocator.Persistent);
_springs = new NativeArray<BlittableSpring>(springsCount, Allocator.Persistent);
_joints = new NativeArray<BlittableJoint>(logicsCount, Allocator.Persistent);
_logics = new NativeArray<BlittableLogic>(logicsCount, Allocator.Persistent);
_colliders = new NativeArray<BlittableCollider>(collidersCount, Allocator.Persistent);
_transforms = new NativeArray<BlittableTransform>(transformsCount, Allocator.Persistent);
Profiler.EndSample();
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
var springsOffset = 0;
var collidersOffset = 0;
var logicsOffset = 0;
var transformOffset = 0;
Profiler.BeginSample("FastSpringBone.ReconstructBuffers.ScheduleLoadBufferJobs");
for (var i = 0; i < _batchedBuffers.Length; i++)
{
var buffer = _batchedBuffers[i];
@@ -151,10 +176,10 @@ namespace UniGLTF.SpringBoneJobs
handle = new LoadTransformsJob
{
SrcTransforms = buffer.BlittableTransforms,
DestTransforms =
new NativeSlice<BlittableTransform>(_transforms, transformOffset,
DestTransforms = new NativeSlice<BlittableTransform>(_transforms, transformOffset,
buffer.BlittableTransforms.Length)
}.Schedule(buffer.BlittableTransforms.Length, 1, handle);
handle = new LoadSpringsJob
{
SrcSprings = buffer.Springs,
@@ -163,18 +188,20 @@ namespace UniGLTF.SpringBoneJobs
LogicsOffset = logicsOffset,
TransformOffset = transformOffset,
}.Schedule(buffer.Springs.Length, 1, handle);
handle = new LoadCollidersJob
{
SrcColliders = buffer.Colliders,
DestColliders =
new NativeSlice<BlittableCollider>(_colliders, collidersOffset, buffer.Colliders.Length)
DestColliders = new NativeSlice<BlittableCollider>(_colliders, collidersOffset, buffer.Colliders.Length)
}.Schedule(buffer.Colliders.Length, 1, handle);
handle = new OffsetLogicsJob
{
SrcLogics = buffer.Logics,
SrcJoints = buffer.Joints,
DestLogics = new NativeSlice<BlittableLogic>(_logics, logicsOffset, buffer.Logics.Length),
DestJoints = new NativeSlice<BlittableJoint>(_joints, logicsOffset, buffer.Logics.Length),
DestLogics = new NativeSlice<BlittableJointImmutable>(_logics, logicsOffset, buffer.Logics.Length),
DestJoints = new NativeSlice<BlittableJointMutable>(_joints, logicsOffset, buffer.Logics.Length),
}.Schedule(buffer.Logics.Length, 1, handle);
springsOffset += buffer.Springs.Length;
@@ -183,6 +210,8 @@ namespace UniGLTF.SpringBoneJobs
transformOffset += buffer.BlittableTransforms.Length;
}
handle = InitCurrentTails(handle);
// TransformAccessArrayの構築と並行してJobを行うため、この時点で走らせておく
JobHandle.ScheduleBatchedJobs();
Profiler.EndSample();
@@ -205,14 +234,35 @@ namespace UniGLTF.SpringBoneJobs
return handle;
}
/// <summary>
/// Transform から currentTail を更新。
/// prevTail も同じ内容にする(速度0)。
/// </summary>
/// <param name="handle"></param>
/// <returns></returns>
public JobHandle InitCurrentTails(JobHandle handle)
{
return new InitCurrentTailsJob
{
Logics = Logics,
Transforms = Transforms,
CurrentTails = CurrentTails,
PrevTails = PrevTails,
NextTails = NextTails,
}.Schedule(Logics.Length, 1, handle);
}
private void DisposeAllBuffers()
{
if (_springs.IsCreated) _springs.Dispose();
if (_logics.IsCreated) _logics.Dispose();
if (_joints.IsCreated) _joints.Dispose();
if (_prevTails.IsCreated) _prevTails.Dispose();
if (_currentTails.IsCreated) _currentTails.Dispose();
if (_nextTails.IsCreated) _nextTails.Dispose();
if (_springs.IsCreated) _springs.Dispose();
if (_colliders.IsCreated) _colliders.Dispose();
if (_transforms.IsCreated) _transforms.Dispose();
if (_transformAccessArray.isCreated) _transformAccessArray.Dispose();
if (_colliders.IsCreated) _colliders.Dispose();
if (_logics.IsCreated) _logics.Dispose();
}
public void Dispose()
@@ -275,10 +325,10 @@ namespace UniGLTF.SpringBoneJobs
#endif
private struct OffsetLogicsJob : IJobParallelFor
{
[ReadOnly] public NativeSlice<BlittableLogic> SrcLogics;
[ReadOnly] public NativeSlice<BlittableJoint> SrcJoints;
[WriteOnly] public NativeSlice<BlittableLogic> DestLogics;
[WriteOnly] public NativeSlice<BlittableJoint> DestJoints;
[ReadOnly] public NativeSlice<BlittableJointImmutable> SrcLogics;
[ReadOnly] public NativeSlice<BlittableJointMutable> SrcJoints;
[WriteOnly] public NativeSlice<BlittableJointImmutable> DestLogics;
[WriteOnly] public NativeSlice<BlittableJointMutable> DestJoints;
public void Execute(int index)
{
@@ -286,5 +336,37 @@ namespace UniGLTF.SpringBoneJobs
DestJoints[index] = SrcJoints[index];
}
}
#if ENABLE_SPRINGBONE_BURST
[BurstCompile]
#endif
private struct InitCurrentTailsJob : IJobParallelFor
{
[ReadOnly] public NativeArray<BlittableJointImmutable> Logics;
[ReadOnly] public NativeArray<BlittableTransform> Transforms;
[WriteOnly] public NativeSlice<Vector3> CurrentTails;
[WriteOnly] public NativeSlice<Vector3> PrevTails;
[WriteOnly] public NativeSlice<Vector3> NextTails;
public void Execute(int jointIndex)
{
var tailIndex = Logics[jointIndex].tailTransformIndex;
if (tailIndex == -1)
{
// 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;
}
}
}
}
}

View File

@@ -31,12 +31,17 @@ namespace UniGLTF.SpringBoneJobs
Transforms = _bufferCombiner.Transforms
}.Schedule(_bufferCombiner.TransformAccessArray, handle);
_bufferCombiner.FlipBuffer();
handle = new UpdateFastSpringBoneJob
{
Colliders = _bufferCombiner.Colliders,
Joints = _bufferCombiner.Joints,
Logics = _bufferCombiner.Logics,
CurrentTail = _bufferCombiner.CurrentTails,
PrevTail = _bufferCombiner.PrevTails,
NextTail = _bufferCombiner.NextTails,
Springs = _bufferCombiner.Springs,
Colliders = _bufferCombiner.Colliders,
Transforms = _bufferCombiner.Transforms,
DeltaTime = deltaTime,
}.Schedule(_bufferCombiner.Springs.Length, 1, handle);

View File

@@ -16,9 +16,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
{
// NOTE: これらはFastSpringBoneBufferCombinerによってバッチングされる
public NativeArray<BlittableSpring> Springs { get; }
public NativeArray<BlittableJoint> Joints { get; }
public NativeArray<BlittableJointMutable> Joints { get; }
public NativeArray<BlittableCollider> Colliders { get; }
public NativeArray<BlittableLogic> Logics { get; }
public NativeArray<BlittableJointImmutable> Logics { get; }
public NativeArray<BlittableTransform> BlittableTransforms { get; }
public Transform[] Transforms { get; }
public bool IsDisposed { get; private set; }
@@ -92,9 +92,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
var externalDataPtr = (BlittableExternalData*)_externalData.GetUnsafePtr();
List<BlittableSpring> blittableSprings = new();
List<BlittableJoint> blittableJoints = new();
List<BlittableJointMutable> blittableJoints = new();
List<BlittableCollider> blittableColliders = new();
List<BlittableLogic> blittableLogics = new();
List<BlittableJointImmutable> blittableLogics = new();
foreach (var spring in springs)
{
var blittableSpring = new BlittableSpring
@@ -131,9 +131,9 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
}
Springs = new NativeArray<BlittableSpring>(blittableSprings.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJoint>(blittableJoints.ToArray(), Allocator.Persistent);
Joints = new NativeArray<BlittableJointMutable>(blittableJoints.ToArray(), Allocator.Persistent);
Colliders = new NativeArray<BlittableCollider>(blittableColliders.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableLogic>(blittableLogics.ToArray(), Allocator.Persistent);
Logics = new NativeArray<BlittableJointImmutable>(blittableLogics.ToArray(), Allocator.Persistent);
BlittableTransforms = new NativeArray<BlittableTransform>(Transforms.Length, Allocator.Persistent);
Profiler.EndSample();
}
@@ -145,15 +145,17 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
/// <param name="spring"></param>
/// <param name="i">joint index</param>
/// <returns></returns>
public static IEnumerable<BlittableLogic> LogicFromTransform(Transform[] Transforms, FastSpringBoneSpring spring)
public static IEnumerable<BlittableJointImmutable> LogicFromTransform(Transform[] Transforms, FastSpringBoneSpring spring)
{
// vrm-1.0 では末端の joint は tail で処理対象でないのに注意!
for (int i = 0; i < spring.joints.Length - 1; ++i)
{
var joint = spring.joints[i];
var tailJoint = i + 1 < spring.joints.Length ? spring.joints[i + 1] : (FastSpringBoneJoint?)null;
Debug.Assert(i + 1 < spring.joints.Length);
var tailJoint = (i + 1 < spring.joints.Length) ? spring.joints[i + 1] : (FastSpringBoneJoint?)null;
Debug.Assert(tailJoint.HasValue);
var parentJoint = i - 1 >= 0 ? spring.joints[i - 1] : (FastSpringBoneJoint?)null;
var localPosition = Vector3.zero;
Vector3 localPosition;
if (tailJoint.HasValue)
{
localPosition = tailJoint.Value.Transform.localPosition;
@@ -178,19 +180,13 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
localPosition.y * scale.y,
localPosition.z * scale.z
);
var worldChildPosition = joint.Transform.TransformPoint(localChildPosition);
var currentTail = spring.center != null
? spring.center.InverseTransformPoint(worldChildPosition)
: worldChildPosition;
var parent = joint.Transform.parent;
yield return new BlittableLogic
yield return new BlittableJointImmutable
{
headTransformIndex = Array.IndexOf(Transforms, joint.Transform),
parentTransformIndex = Array.IndexOf(Transforms, parent),
currentTail = currentTail,
prevTail = currentTail, // same with currentTail. velocity zero.
tailTransformIndex = Array.IndexOf(Transforms, tailJoint.Value),
localRotation = joint.DefaultLocalRotation,
boneAxis = localChildPosition.normalized,
length = localChildPosition.magnitude
@@ -209,16 +205,5 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
Logics.Dispose();
_externalData.Dispose();
}
public void SyncAndZeroVelocity(IReadOnlyList<BlittableLogic> logics)
{
var dst = Logics;
for (int i = 0; i < logics.Count; ++i)
{
var l = logics[i];
l.prevTail = l.currentTail;
dst[i] = l;
}
}
}
}

View File

@@ -8,7 +8,7 @@ namespace UniGLTF.SpringBoneJobs.InputPorts
public struct FastSpringBoneJoint
{
public Transform Transform;
public BlittableJoint Joint;
public BlittableJointMutable Joint;
public Quaternion DefaultLocalRotation;
}
}

View File

@@ -14,11 +14,21 @@ namespace UniGLTF.SpringBoneJobs
#endif
public struct UpdateFastSpringBoneJob : IJobParallelFor
{
// すべての spring の joint を平坦に連結した配列
// Joints, Logics, PrevTail, CurrentTail, NextTail は同じ index
[ReadOnly] public NativeArray<BlittableJointMutable> Joints;
[ReadOnly] public NativeArray<BlittableJointImmutable> Logics;
[ReadOnly] public NativeArray<Vector3> PrevTail;
[ReadOnly] public NativeArray<Vector3> CurrentTail;
// ランダムアクセス
[NativeDisableParallelForRestriction] public NativeArray<Vector3> NextTail;
[ReadOnly] public NativeArray<BlittableSpring> Springs;
[ReadOnly] public NativeArray<BlittableJoint> Joints;
[ReadOnly] public NativeArray<BlittableCollider> Colliders;
[NativeDisableParallelForRestriction] public NativeArray<BlittableLogic> Logics;
// FastSpringBoneBuffer.Transforms を連結したもの
// ランダムアクセス
[NativeDisableParallelForRestriction] public NativeArray<BlittableTransform> Transforms;
public float DeltaTime;
@@ -52,11 +62,11 @@ namespace UniGLTF.SpringBoneJobs
}
var currentTail = centerTransform.HasValue
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(logic.currentTail)
: logic.currentTail;
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(CurrentTail[logicIndex])
: CurrentTail[logicIndex];
var prevTail = centerTransform.HasValue
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(logic.prevTail)
: logic.prevTail;
? centerTransform.Value.localToWorldMatrix.MultiplyPoint3x4(PrevTail[logicIndex])
: PrevTail[logicIndex];
var parentRotation = parentTransform?.rotation ?? Quaternion.identity;
@@ -108,14 +118,10 @@ namespace UniGLTF.SpringBoneJobs
}
}
logic.prevTail = centerTransform.HasValue
? centerTransform.Value.worldToLocalMatrix.MultiplyPoint3x4(currentTail)
: currentTail;
logic.currentTail = centerTransform.HasValue
NextTail[logicIndex] = centerTransform.HasValue
? centerTransform.Value.worldToLocalMatrix.MultiplyPoint3x4(nextTail)
: nextTail;
//回転を適用
var rotation = parentRotation * logic.localRotation;
headTransform.rotation = Quaternion.FromToRotation(rotation * logic.boneAxis,
@@ -157,7 +163,6 @@ namespace UniGLTF.SpringBoneJobs
// SpringBone の結果を Transform に反映しないが logic の更新は継続する。
// 再開したときに暴れない。
}
Logics[logicIndex] = logic;
}
}
@@ -174,10 +179,10 @@ namespace UniGLTF.SpringBoneJobs
Vector3 worldTail,
Vector3 worldPosition,
BlittableTransform headTransform,
BlittableJoint joint,
BlittableJointMutable joint,
BlittableCollider collider,
float maxColliderScale,
BlittableLogic logic,
BlittableJointImmutable logic,
ref Vector3 nextTail)
{
var direction = worldTail - worldPosition;
@@ -209,12 +214,12 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveSphereCollision(
BlittableJoint joint,
BlittableJointMutable joint,
BlittableCollider collider,
Vector3 worldPosition,
BlittableTransform headTransform,
float maxColliderScale,
BlittableLogic logic,
BlittableJointImmutable logic,
ref Vector3 nextTail)
{
var r = joint.radius + collider.radius * maxColliderScale;
@@ -229,7 +234,7 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveSphereCollisionInside(
BlittableJoint joint,
BlittableJointMutable joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
@@ -249,7 +254,7 @@ namespace UniGLTF.SpringBoneJobs
}
private static void ResolveCapsuleCollisionInside(
BlittableJoint joint,
BlittableJointMutable joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)
@@ -298,7 +303,7 @@ namespace UniGLTF.SpringBoneJobs
/// <param name="colliderTransform">colliderTransform.localToWorldMatrix.MultiplyPoint3x4(collider.offset);</param>
/// <param name="nextTail">result of verlet integration</param>
private static void ResolvePlaneCollision(
BlittableJoint joint,
BlittableJointMutable joint,
BlittableCollider collider,
BlittableTransform colliderTransform,
ref Vector3 nextTail)

View File

@@ -87,7 +87,7 @@ namespace VRM.SpringBoneJobs
joints.Add(new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
Joint = new BlittableJointMutable
{
radius = spring.m_hitRadius,
dragForce = spring.m_dragForce,

View File

@@ -92,7 +92,7 @@ namespace UniVRM10
.Select(joint => new FastSpringBoneJoint
{
Transform = joint.transform,
Joint = new BlittableJoint
Joint = new BlittableJointMutable
{
radius = joint.m_jointRadius,
dragForce = joint.m_dragForce,
@@ -150,15 +150,7 @@ namespace UniVRM10
transform.localRotation = m_initialLocalRotations[i];
}
// 初期状態にしたtransformを使って spring logic を構築
List<BlittableLogic> blittableLogics = new();
foreach (var spring in m_springs)
{
blittableLogics.AddRange(
FastSpringBoneBuffer.LogicFromTransform(m_fastSpringBoneBuffer.Transforms, spring));
}
// DOTS バッファーを更新
m_fastSpringBoneBuffer.SyncAndZeroVelocity(blittableLogics);
// TODO:
}
}
}