diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller.meta new file mode 100644 index 000000000..f759ea07f --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ea8ee11da1da4f82b988a3108a362c2f +timeCreated: 1634202988 \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/IAwaitCaller.cs b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs similarity index 70% rename from Assets/VRMShaders/GLTF/IO/Runtime/IAwaitCaller.cs rename to Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs index 2d31a73d5..c97e4d0ce 100644 --- a/Assets/VRMShaders/GLTF/IO/Runtime/IAwaitCaller.cs +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs @@ -32,26 +32,4 @@ namespace VRMShaders /// Task Run(Func action); } - - /// - /// 同期実行 - /// - public sealed class ImmediateCaller : IAwaitCaller - { - public Task NextFrame() - { - return Task.FromResult(null); - } - - public Task Run(Action action) - { - action(); - return Task.FromResult(null); - } - - public Task Run(Func action) - { - return Task.FromResult(action()); - } - } } diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/IAwaitCaller.cs.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs.meta similarity index 100% rename from Assets/VRMShaders/GLTF/IO/Runtime/IAwaitCaller.cs.meta rename to Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs.meta diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs new file mode 100644 index 000000000..f3e6d56a0 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; + +namespace VRMShaders +{ + /// + /// 同期実行 + /// + public sealed class ImmediateCaller : IAwaitCaller + { + public Task NextFrame() + { + return Task.FromResult(null); + } + + public Task Run(Action action) + { + action(); + return Task.FromResult(null); + } + + public Task Run(Func action) + { + return Task.FromResult(action()); + } + } +} \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs.meta new file mode 100644 index 000000000..a128df3a9 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30f43d5b7d6b4184fb775a315b835365 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs new file mode 100644 index 000000000..27df7af6a --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs @@ -0,0 +1,54 @@ +using System; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace VRMShaders +{ + internal sealed class NextFrameTaskScheduler + { + public bool IsSupported => Application.isPlaying; + + public NextFrameTaskScheduler() + { + if (!IsSupported) + { + throw new NotSupportedException($"{nameof(NextFrameTaskScheduler)} is supported at runtime only."); + } + } + + public bool Enqueue(Action action) + { + var currentFrame = Time.frameCount; + + UnityLoopTaskScheduler.Instance.Scheduler.Enqueue(action, () => Time.frameCount != currentFrame); + + return true; + } + + private sealed class UnityLoopTaskScheduler : MonoBehaviour + { + private static UnityLoopTaskScheduler _instance; + + public static UnityLoopTaskScheduler Instance + { + get + { + if (_instance == null) + { + var go = new GameObject("UniGLTF UnityThreadScheduler"); + Object.DontDestroyOnLoad(go); + _instance = go.AddComponent(); + } + return _instance; + } + } + + public TinyManagedTaskScheduler Scheduler { get; } = new TinyManagedTaskScheduler(); + + private void Update() + { + Scheduler.ManagedUpdate(); + } + } + } +} \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs.meta new file mode 100644 index 000000000..02892074e --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/NextFrameTaskScheduler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3ce7defdc7144878a058274ae561d0be +timeCreated: 1634203513 \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs new file mode 100644 index 000000000..23cc7b592 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; + +namespace VRMShaders +{ + /// + /// Runtime (Build 後と、Editor Playing) での非同期ロードを実現する AwaitCaller. + /// NOTE: 簡便に実装されたものなので、最適化の余地はある. + /// + public sealed class RuntimeOnlyAwaitCaller : IAwaitCaller + { + private readonly NextFrameTaskScheduler _scheduler; + + public RuntimeOnlyAwaitCaller() + { + _scheduler = new NextFrameTaskScheduler(); + } + + public Task NextFrame() + { + var tcs = new TaskCompletionSource(); + _scheduler.Enqueue(() => tcs.SetResult(default)); + return tcs.Task; + } + + public Task Run(Action action) + { + return Task.Run(action); + } + + public Task Run(Func action) + { + return Task.Run(action); + } + } +} \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta new file mode 100644 index 000000000..a9e2c5963 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e4e64b1835ca4cd2b682a6bda572b8fb +timeCreated: 1634203216 \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs new file mode 100644 index 000000000..820feb7f9 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Concurrent; + +namespace VRMShaders +{ + internal sealed class TinyManagedTaskScheduler + { + private readonly ConcurrentQueue<(Action, Func)> _continuationQueue = + new ConcurrentQueue<(Action, Func)>(); + private readonly ConcurrentQueue<(Action, Func)> _temporaryQueue = + new ConcurrentQueue<(Action, Func)>(); + + public void ManagedUpdate() + { + while (_continuationQueue.TryDequeue(out var tuple)) + { + var (continuation, canExecute) = tuple; + + if (canExecute()) + { + continuation(); + } + else + { + _temporaryQueue.Enqueue(tuple); + } + } + + while (_temporaryQueue.TryDequeue(out var tuple)) + { + _continuationQueue.Enqueue(tuple); + } + } + + public void Enqueue(Action continuation, Func canExecute) + { + _continuationQueue.Enqueue((continuation, canExecute)); + } + } +} \ No newline at end of file diff --git a/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs.meta b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs.meta new file mode 100644 index 000000000..3c81f82e0 --- /dev/null +++ b/Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/TinyManagedTaskScheduler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9c52ff07a0834bdc8f4c8d6c7f9a634f +timeCreated: 1634203919 \ No newline at end of file