Skip to content

Commit

Permalink
Merge pull request #114 from AnnulusGames/optimize-motionstorage
Browse files Browse the repository at this point in the history
Optimize: MotionStorage and MotionBuilder
  • Loading branch information
AnnulusGames authored Mar 20, 2024
2 parents 115e51d + 5e63a17 commit b2684c1
Show file tree
Hide file tree
Showing 29 changed files with 333 additions and 295 deletions.
11 changes: 10 additions & 1 deletion src/LitMotion/Assets/LitMotion/Runtime/CancelBehaviour.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public static Observable<TValue> ToObservable<TValue, TOptions, TAdapter>(this M
where TAdapter : unmanaged, IMotionAdapter<TValue, TOptions>
{
var subject = new Subject<TValue>();
var callbacks = builder.BuildCallbackData(subject, static (x, subject) => subject.OnNext(x));
callbacks.OnCompleteAction += () => subject.OnCompleted();
callbacks.OnCancelAction += () => subject.OnCompleted();
builder.SetCallbackData(subject, static (x, subject) => subject.OnNext(x));
builder.buffer.CallbackData.OnCompleteAction += () => subject.OnCompleted();
builder.buffer.CallbackData.OnCancelAction += () => subject.OnCompleted();
var scheduler = builder.buffer.Scheduler;
var entity = builder.BuildMotionData();
builder.SetMotionData();

builder.Schedule(scheduler, ref entity, ref callbacks);
builder.Schedule(scheduler, ref builder.buffer.Data, ref builder.buffer.CallbackData);
return subject;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ public static IObservable<TValue> ToObservable<TValue, TOptions, TAdapter>(this
where TAdapter : unmanaged, IMotionAdapter<TValue, TOptions>
{
var subject = new Subject<TValue>();
var callbacks = builder.BuildCallbackData(subject, static (x, subject) => subject.OnNext(x));
callbacks.OnCompleteAction += () => subject.OnCompleted();
callbacks.OnCancelAction += () => subject.OnCompleted();
builder.SetCallbackData(subject, static (x, subject) => subject.OnNext(x));
builder.buffer.CallbackData.OnCompleteAction += () => subject.OnCompleted();
builder.buffer.CallbackData.OnCancelAction += () => subject.OnCompleted();
var scheduler = builder.buffer.Scheduler;
var entity = builder.BuildMotionData();
builder.SetMotionData();

builder.Schedule(scheduler, ref entity, ref callbacks);
builder.Schedule(scheduler, ref builder.buffer.Data, ref builder.buffer.CallbackData);
return subject;
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/Internal/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,10 @@ public static void ArgumentNull(string message)
{
throw new ArgumentNullException(message);
}

public static void PlaybackSpeedMustBeZeroOrGreater()
{
throw new ArgumentOutOfRangeException("Playback speed must be 0 or greater.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ public void InvokeUnsafe<TValue>(in TValue value) where TValue : unmanaged
break;
}
}

public readonly static MotionCallbackData Default = new()
{
SkipValuesDuringDelay = true,
};
}
}
35 changes: 27 additions & 8 deletions src/LitMotion/Assets/LitMotion/Runtime/Internal/MotionData.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LitMotion.Collections;

namespace LitMotion
{
/// <summary>
/// A structure representing motion data.
/// </summary>
/// <typeparam name="TValue">The type of value to animate</typeparam>
/// <typeparam name="TOptions">The type of special parameters given to the motion data</typeparam>
[StructLayout(LayoutKind.Sequential)]
public struct MotionData<TValue, TOptions>
where TValue : unmanaged
where TOptions : unmanaged, IMotionOptions
public struct MotionDataCore
{
public MotionStatus Status;

Expand All @@ -28,8 +22,33 @@ public struct MotionData<TValue, TOptions>
public DelayType DelayType;
public LoopType LoopType;

public static readonly MotionDataCore Default = new()
{
Loops = 1,
PlaybackSpeed = 1f,
};
}

/// <summary>
/// A structure representing motion data.
/// </summary>
/// <typeparam name="TValue">The type of value to animate</typeparam>
/// <typeparam name="TOptions">The type of special parameters given to the motion data</typeparam>
[StructLayout(LayoutKind.Sequential)]
public struct MotionData<TValue, TOptions>
where TValue : unmanaged
where TOptions : unmanaged, IMotionOptions
{
// Because of pointer casting, this field must always be placed at the beginning.
public MotionDataCore Core;

public TValue StartValue;
public TValue EndValue;
public TOptions Options;

public static readonly MotionData<TValue, TOptions> Default = new()
{
Core = MotionDataCore.Default,
};
}
}
61 changes: 24 additions & 37 deletions src/LitMotion/Assets/LitMotion/Runtime/Internal/MotionStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Runtime.CompilerServices;
using LitMotion.Collections;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

// TODO: Constantize the exception message

Expand Down Expand Up @@ -35,10 +36,8 @@ internal unsafe interface IMotionStorage
bool IsActive(MotionHandle handle);
void Cancel(MotionHandle handle);
void Complete(MotionHandle handle);
float GetPlaybackSpeed(MotionHandle handle);
void SetPlaybackSpeed(MotionHandle handle, float value);
MotionCallbackData GetMotionCallbacks(MotionHandle handle);
void SetMotionCallbacks(MotionHandle handle, MotionCallbackData callbacks);
ref MotionDataCore GetDataRef(MotionHandle handle);
ref MotionCallbackData GetCallbackDataRef(MotionHandle handle);
void Reset();
}

Expand Down Expand Up @@ -166,21 +165,21 @@ public MotionStorage(int id)
UnityEngine.Debug.Log("[Add] Entry:" + entryIndex + " Dense:" + entry.DenseIndex + " Version:" + entry.Version);
#endif

var prevAnimationCurve = dataArray[tail].AnimationCurve;
var prevAnimationCurve = dataArray[tail].Core.AnimationCurve;

toEntryIndex[tail] = entryIndex;
dataArray[tail] = data;
callbacksArray[tail] = callbacks;

if (data.Ease == Ease.CustomAnimationCurve)
if (data.Core.Ease == Ease.CustomAnimationCurve)
{
if (!prevAnimationCurve.IsCreated)
{
prevAnimationCurve = new NativeAnimationCurve(AllocatorHelper.Allocator.Handle);
}

prevAnimationCurve.CopyFrom(data.AnimationCurve);
dataArray[tail].AnimationCurve = prevAnimationCurve;
prevAnimationCurve.CopyFrom(data.Core.AnimationCurve);
dataArray[tail].Core.AnimationCurve = prevAnimationCurve;
}

tail++;
Expand Down Expand Up @@ -266,12 +265,12 @@ public void Cancel(MotionHandle handle)

ref var motion = ref GetDataSpan()[denseIndex];
var version = entry.Version;
if (version <= 0 || version != handle.Version || motion.Status == MotionStatus.None)
if (version <= 0 || version != handle.Version || motion.Core.Status == MotionStatus.None)
{
throw new ArgumentException("Motion has been destroyed or no longer exists.");
}

motion.Status = MotionStatus.Canceled;
motion.Core.Status = MotionStatus.Canceled;

ref var callbackData = ref GetCallbacksSpan()[denseIndex];
try
Expand All @@ -295,12 +294,12 @@ public void Complete(MotionHandle handle)

ref var motion = ref GetDataSpan()[denseIndex];
var version = entry.Version;
if (version <= 0 || version != handle.Version || motion.Status == MotionStatus.None)
if (version <= 0 || version != handle.Version || motion.Core.Status == MotionStatus.None)
{
throw new ArgumentException("Motion has been destroyed or no longer exists.");
}

if (motion.Loops < 0)
if (motion.Core.Loops < 0)
{
UnityEngine.Debug.LogWarning("[LitMotion] Complete was ignored because it is not possible to complete a motion that loops infinitely. If you want to end the motion, call Cancel() instead.");
return;
Expand All @@ -314,20 +313,20 @@ public void Complete(MotionHandle handle)
callbackData.IsCallbackRunning = true;

// To avoid duplication of Complete processing, it is treated as canceled internally.
motion.Status = MotionStatus.Canceled;
motion.Core.Status = MotionStatus.Canceled;

var endProgress = motion.LoopType switch
var endProgress = motion.Core.LoopType switch
{
LoopType.Restart => 1f,
LoopType.Yoyo => motion.Loops % 2 == 0 ? 0f : 1f,
LoopType.Incremental => motion.Loops,
LoopType.Yoyo => motion.Core.Loops % 2 == 0 ? 0f : 1f,
LoopType.Incremental => motion.Core.Loops,
_ => 1f
};

var easedEndProgress = motion.Ease switch
var easedEndProgress = motion.Core.Ease switch
{
Ease.CustomAnimationCurve => motion.AnimationCurve.Evaluate(endProgress),
_ => EaseUtility.Evaluate(endProgress, motion.Ease),
Ease.CustomAnimationCurve => motion.Core.AnimationCurve.Evaluate(endProgress),
_ => EaseUtility.Evaluate(endProgress, motion.Core.Ease),
};

var endValue = default(TAdapter).Evaluate(
Expand Down Expand Up @@ -367,19 +366,19 @@ public bool IsActive(MotionHandle handle)
var version = entry.Version;
if (version <= 0 || version != handle.Version) return false;
var motion = dataArray[denseIndex];
return motion.Status is MotionStatus.Scheduled or MotionStatus.Delayed or MotionStatus.Playing;
return motion.Core.Status is MotionStatus.Scheduled or MotionStatus.Delayed or MotionStatus.Playing;
}

public MotionCallbackData GetMotionCallbacks(MotionHandle handle)
public ref MotionCallbackData GetCallbackDataRef(MotionHandle handle)
{
CheckIndex(handle);
return callbacksArray[entries[handle.Index].DenseIndex];
return ref callbacksArray[entries[handle.Index].DenseIndex];
}

public void SetMotionCallbacks(MotionHandle handle, MotionCallbackData callbacks)
public ref MotionDataCore GetDataRef(MotionHandle handle)
{
CheckIndex(handle);
callbacksArray[entries[handle.Index].DenseIndex] = callbacks;
return ref UnsafeUtility.As<MotionData<TValue, TOptions>, MotionDataCore>(ref dataArray[entries[handle.Index].DenseIndex]);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -393,7 +392,7 @@ void CheckIndex(MotionHandle handle)
}

var version = entry.Version;
if (version <= 0 || version != handle.Version || dataArray[denseIndex].Status == MotionStatus.None)
if (version <= 0 || version != handle.Version || dataArray[denseIndex].Core.Status == MotionStatus.None)
{
throw new ArgumentException("Motion has been destroyed or no longer exists.");
}
Expand All @@ -410,17 +409,5 @@ public void Reset()

AllocatorHelper.Allocator.Rewind();
}

public float GetPlaybackSpeed(MotionHandle handle)
{
CheckIndex(handle);
return dataArray[entries[handle.Index].DenseIndex].PlaybackSpeed;
}

public void SetPlaybackSpeed(MotionHandle handle, float value)
{
CheckIndex(handle);
dataArray[entries[handle.Index].DenseIndex].PlaybackSpeed = value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,16 @@ public static bool IsActive(MotionHandle handle)
return storageList[handle.StorageId].IsActive(handle);
}

public static float GetMotionPlaybackSpeed(MotionHandle handle)
public static ref MotionDataCore GetMotionDataRef(MotionHandle handle)
{
CheckStorageId(handle);
return storageList[handle.StorageId].GetPlaybackSpeed(handle);
return ref storageList[handle.StorageId].GetDataRef(handle);
}

public static void SetMotionPlaybackSpeed(MotionHandle handle, float value)
public static ref MotionCallbackData GetMotionCallbackDataRef(MotionHandle handle)
{
if (value < 0f) throw new ArgumentOutOfRangeException("Playback speed must be 0 or greater.");
CheckStorageId(handle);
storageList[handle.StorageId].SetPlaybackSpeed(handle, value);
}

public static MotionCallbackData GetMotionCallbacks(MotionHandle handle)
{
CheckStorageId(handle);
return storageList[handle.StorageId].GetMotionCallbacks(handle);
}

public static void SetMotionCallbacks(MotionHandle handle, MotionCallbackData callbacks)
{
CheckStorageId(handle);
storageList[handle.StorageId].SetMotionCallbacks(handle, callbacks);
return ref storageList[handle.StorageId].GetCallbackDataRef(handle);
}

// For MotionTracker
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b2684c1

Please sign in to comment.