Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: LitMotion.Sequences #158

Merged
merged 18 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("LitMotion.Sequences")]
[assembly: InternalsVisibleTo("LitMotion.Extensions")]
[assembly: InternalsVisibleTo("LitMotion.Editor")]
[assembly: InternalsVisibleTo("LitMotion.Tests.Runtime")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct MotionDataCore
public float PlaybackSpeed;
public bool IsPreserved;
public bool WasStatusChanged;
public bool SkipUpdate;

// parameters
public float Duration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ public static void SetTime(MotionHandle handle, double time)
list[handle.StorageId].SetTime(handle, time);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void AddToSequence(ref MotionHandle handle, out double motionDuration)
{
CheckTypeId(handle);
list[handle.StorageId].AddToSequence(ref handle, out motionDuration);
}

// For MotionTracker
public static (Type ValueType, Type OptionsType, Type AdapterType) GetMotionType(MotionHandle handle)
{
Expand Down
29 changes: 28 additions & 1 deletion src/LitMotion/Assets/LitMotion/Runtime/Internal/MotionStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ internal interface IMotionStorage
void Cancel(MotionHandle handle);
void Complete(MotionHandle handle);
void SetTime(MotionHandle handle, double time);
void AddToSequence(ref MotionHandle handle, out double motionDuration);
ref MotionDataCore GetDataRef(MotionHandle handle);
ref ManagedMotionData GetManagedDataRef(MotionHandle handle);
void Reset();
}


internal sealed class MotionStorage<TValue, TOptions, TAdapter> : IMotionStorage
where TValue : unmanaged
where TOptions : unmanaged, IMotionOptions
Expand Down Expand Up @@ -414,6 +414,33 @@ public unsafe void SetTime(MotionHandle handle, double time)
}
}

public void AddToSequence(ref MotionHandle handle, out double motionDuration)
{
ref var slot = ref GetSlotWithVarify(handle);
ref var dataRef = ref unmanagedDataArray[slot.DenseIndex];

if (dataRef.Core.Status is not MotionStatus.Scheduled)
{
throw new ArgumentException("Cannot add a running motion to a sequence.");
}

motionDuration = handle.Duration;
if (double.IsInfinity(motionDuration))
{
throw new ArgumentException("Cannot add an infinitely looping motion to a sequence.");
}

dataRef.Core.IsPreserved = true;
dataRef.Core.SkipUpdate = true;

// ref var managedDataRef = ref managedDataArray[slot.DenseIndex];
// managedDataRef.SkipValuesDuringDelay = true;

// increment version
slot.Version++;
handle.Version++;
}

public ref ManagedMotionData GetManagedDataRef(MotionHandle handle)
{
ref var slot = ref GetSlotWithVarify(handle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public unsafe void Update(double time, double unscaledTime, double realtime)
for (int i = 0; i < managedDataSpan.Length; i++)
{
var currentDataPtr = dataPtr + i;

if (currentDataPtr->Core.SkipUpdate) continue;

var status = currentDataPtr->Core.Status;
ref var managedData = ref managedDataSpan[i];
if (status == MotionStatus.Playing || (status == MotionStatus.Delayed && !managedData.SkipValuesDuringDelay))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static void Preserve(this MotionHandle handle)
{
MotionManager.GetDataRef(handle).IsPreserved = true;
}

/// <summary>
/// Complete motion.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/MotionUpdateJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public void Execute([AssumeRange(0, int.MaxValue)] int index)
if (Hint.Likely(corePtr->Status is MotionStatus.Scheduled or MotionStatus.Delayed or MotionStatus.Playing) ||
Hint.Unlikely(corePtr->IsPreserved && corePtr->Status is MotionStatus.Completed))
{
if (Hint.Unlikely(corePtr->SkipUpdate)) return;

var deltaTime = corePtr->TimeKind switch
{
MotionTimeKind.Time => DeltaTime,
Expand Down
8 changes: 8 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/Sequences.meta

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

11 changes: 11 additions & 0 deletions src/LitMotion/Assets/LitMotion/Runtime/Sequences/LSequence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace LitMotion.Sequences
{
public static class LSequence
{
public static MotionSequenceBuilder Create()
{
var source = MotionSequenceBuilderSource.Rent();
return new MotionSequenceBuilder(source);
}
}
}

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
@@ -0,0 +1,16 @@
{
"name": "LitMotion.Sequences",
"rootNamespace": "",
"references": [
"GUID:3b570a5146f9d4f0fa107ed4559471a3"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

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
@@ -0,0 +1,173 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using LitMotion.Collections;

namespace LitMotion.Sequences
{
internal sealed class MotionSequenceBuilderSource : ILinkedPoolNode<MotionSequenceBuilderSource>
{
static LinkedPool<MotionSequenceBuilderSource> pool;

public static MotionSequenceBuilderSource Rent()
{
if (!pool.TryPop(out var result)) result = new();
return result;
}

public static void Return(MotionSequenceBuilderSource source)
{
if (source.buffer != null)
{
ArrayPool<MotionSequenceItem>.Shared.Return(source.buffer);
}

source.version++;
source.buffer = null;
source.tail = 0;
source.lastTail = 0;
source.count = 0;
source.duration = 0;

pool.TryPush(source);
}

public ref MotionSequenceBuilderSource NextNode => ref next;
public ushort Version => version;

MotionSequenceBuilderSource next;
ushort version;
MotionSequenceItem[] buffer;
int count;
double tail;
double lastTail;
double duration;

public void Append(MotionHandle handle)
{
MotionManager.AddToSequence(ref handle, out var motionDuration);
AddItem(new MotionSequenceItem(tail, handle));
AppendInterval(motionDuration);
}

public void AppendInterval(double interval)
{
lastTail = tail;
tail += interval;
duration = Math.Max(duration, tail);
}

public void Insert(double position, MotionHandle handle)
{
MotionManager.AddToSequence(ref handle, out var motionDuration);
AddItem(new MotionSequenceItem(position, handle));
duration = Math.Max(duration, position + motionDuration);
}

public void Join(MotionHandle handle)
{
Insert(lastTail, handle);
}

public MotionHandle Schedule()
{
var source = MotionSequenceSource.Rent();
var handle = LMotion.Create(0.0, duration, (float)duration)
.WithOnComplete(source.OnCompleteDelegate)
.WithOnCancel(source.OnCancelDelegate)
.Bind(source, (x, source) => source.Time = x);

source.Initialize(handle, buffer, count, duration);
buffer = null;
return handle;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void AddItem(in MotionSequenceItem item)
{
if (buffer == null)
{
buffer = ArrayPool<MotionSequenceItem>.Shared.Rent(32);
}
else if (buffer.Length == count)
{
var newBuffer = ArrayPool<MotionSequenceItem>.Shared.Rent(count * 2);
ArrayPool<MotionSequenceItem>.Shared.Return(buffer);
buffer = newBuffer;
}

buffer[count] = item;
count++;
}
}

public struct MotionSequenceBuilder : IDisposable
{
internal MotionSequenceBuilderSource source;
internal ushort version;

internal MotionSequenceBuilder(MotionSequenceBuilderSource source)
{
this.source = source;
this.version = source.Version;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly MotionSequenceBuilder Append(MotionHandle handle)
{
CheckIsDisposed();
source.Append(handle);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly MotionSequenceBuilder AppendInterval(float interval)
{
CheckIsDisposed();
source.AppendInterval(interval);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly MotionSequenceBuilder Insert(float position, MotionHandle handle)
{
CheckIsDisposed();
source.Insert(position, handle);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly MotionSequenceBuilder Join(MotionHandle handle)
{
CheckIsDisposed();
source.Join(handle);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MotionHandle Schedule()
{
CheckIsDisposed();
var handle = source.Schedule();
Dispose();
return handle;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
CheckIsDisposed();
MotionSequenceBuilderSource.Return(source);
source = null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
readonly void CheckIsDisposed()
{
if (source == null || source.Version != version)
{
throw new InvalidOperationException("MotionSequenuceBuilder is either not initialized or has already run.");
}
}
}
}

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

Loading