diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask.meta new file mode 100644 index 000000000..30c250b9b --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 280fce2afcd83f645bdd03f9bf7875c6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs new file mode 100644 index 000000000..25b80d4ab --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs @@ -0,0 +1,120 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace System.Runtime.CompilerServices +{ + public sealed class AsyncMethodBuilderAttribute : Attribute + { + public AsyncMethodBuilderAttribute(Type builderType) + { + BuilderType = builderType; + } + + public Type BuilderType { get; } + } +} + +namespace UniGLTF.ExplicitTask +{ + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + T GetResult(); + } + + public interface IAwaitable + { + IAwaiter GetAwaiter(); + } + + public class Awaiter : IAwaiter + { + Task m_task; + + public bool IsCompleted => m_task.IsCompleted; + + public T GetResult() + { + return m_task.Result; + } + + public void OnCompleted(Action continuation) + { + continuation(); + } + + public Awaiter(Task task) + { + m_task = task; + } + } + + public struct ExplicitTaskMethodBuilder + { + // https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs + private AsyncTaskMethodBuilder _methodBuilder; + + public static ExplicitTaskMethodBuilder Create() => + new ExplicitTaskMethodBuilder { _methodBuilder = AsyncTaskMethodBuilder.Create() }; + + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + { + _methodBuilder.Start(ref stateMachine); + } + + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + _methodBuilder.SetStateMachine(stateMachine); + } + public void SetException(Exception exception) + { + _methodBuilder.SetException(exception); + } + public void SetResult(T result) + { + _methodBuilder.SetResult(result); + } + + public void AwaitOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine); + } + public void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } + + public ExplicitTask Task => new ExplicitTask(_methodBuilder.Task); + } + + [AsyncMethodBuilder(typeof(ExplicitTaskMethodBuilder<>))] + public struct ExplicitTask : IAwaitable + { + private Task _task; + + public ExplicitTask(Task task) => _task = task; + + public static ExplicitTask Delay() + { + var task = Task.FromResult(default); + return new ExplicitTask(task); + } + + public T Result => _task.Result; + + public IAwaiter GetAwaiter() + { + return new Awaiter(_task); + } + } + + public struct Unit { } +} diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs.meta b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs.meta new file mode 100644 index 000000000..5accb0293 --- /dev/null +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ExplicitTask/GenericTask.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd83742422647d4409888a3c5710c14a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs index 96777ce7f..0c8eab460 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContext.cs @@ -4,15 +4,16 @@ using System.Collections.Generic; using UnityEngine; using System.IO; using System.Text; -using System.Collections; using UniJSON; -using System.Threading.Tasks; +using UniGLTF.ExplicitTask; #if UNITY_EDITOR using UnityEditor; #endif namespace UniGLTF { + using Task = ExplicitTask; + /// /// GLTF importer /// @@ -358,7 +359,7 @@ namespace UniGLTF { if (nextFrame == null) { - nextFrame = () => Task.FromResult(null); + nextFrame = () => Task.Delay(); } if (Root == null) @@ -382,7 +383,7 @@ namespace UniGLTF var index = i; using (MeasureTime("ReadMesh")) { - var x = await Task.Run(() => meshImporter.ReadMesh(this, index)); + var x = meshImporter.ReadMesh(this, index); var y = await BuildMeshAsync(nextFrame, x, index); Meshes.Add(y); } @@ -433,15 +434,18 @@ namespace UniGLTF { Debug.Log(GetSpeedLog()); } + + return new Unit(); } protected virtual async Task OnLoadModel(Func nextFrame) { Root.name = "GLTF"; await nextFrame(); + return new Unit(); } - async Task BuildMeshAsync(Func nextFrame, MeshImporter.MeshContext x, int i) + async ExplicitTask BuildMeshAsync(Func nextFrame, MeshImporter.MeshContext x, int i) { using (MeasureTime("BuildMesh")) { diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContextExtensions.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContextExtensions.cs index 4ed58c24b..470840a6a 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContextExtensions.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/ImporterContextExtensions.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; -using System.Threading.Tasks; + namespace UniGLTF { @@ -30,71 +29,73 @@ namespace UniGLTF self.Root.name = Path.GetFileNameWithoutExtension(path); } - class TemporarySynchronizationContext : SynchronizationContext - { - Queue m_callbacks = new Queue(); + // class TemporarySynchronizationContext : SynchronizationContext + // { + // Queue m_callbacks = new Queue(); - /// - /// await して中断された継続がここにくる。 - /// - /// ex. await Task.Run(() =>{ /* ここ */ }); - /// - /// - /// - public override void Post(SendOrPostCallback d, object state) - { - // Debug.Log($"Post"); - m_callbacks.Enqueue(() => d(state)); - } + // /// + // /// await して中断された継続がここにくる。 + // /// + // /// ex. await Task.Run(() =>{ /* ここ */ }); + // /// + // /// + // /// + // public override void Post(SendOrPostCallback d, object state) + // { + // // Debug.Log($"Post"); + // m_callbacks.Enqueue(() => d(state)); + // } - /// - /// 一個デキューして実行する。 - /// Unityであれば毎フレームこのように消化しているであろう。 - /// - /// https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Scripting/UnitySynchronizationContext.cs - /// - /// - public bool ExecuteOneCallback() - { - while (m_callbacks.Count == 0) - { - // Debug.Log($"empty callbacks"); - return false; - } + // /// + // /// 一個デキューして実行する。 + // /// Unityであれば毎フレームこのように消化しているであろう。 + // /// + // /// https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Scripting/UnitySynchronizationContext.cs + // /// + // /// + // public bool ExecuteOneCallback() + // { + // while (m_callbacks.Count == 0) + // { + // // Debug.Log($"empty callbacks"); + // return false; + // } - var callback = m_callbacks.Dequeue(); - callback(); - return true; - } - } + // var callback = m_callbacks.Dequeue(); + // callback(); + // return true; + // } + // } /// /// Build unity objects from parsed gltf /// public static void Load(this ImporterContext self) { - var tcs = new TemporarySynchronizationContext(); + throw new NotImplementedException(); - // 元に戻すためバックアップ - var backup = SynchronizationContext.Current; - // 一時的に変更 - SynchronizationContext.SetSynchronizationContext(tcs); - try - { - var task = self.LoadAsync(); + // var tcs = new TemporarySynchronizationContext(); - // 中断された await を消化する - while (!task.IsCompleted) - { - // execute synchronous - tcs.ExecuteOneCallback(); - } - } - finally - { - // 元に戻す - SynchronizationContext.SetSynchronizationContext(backup); - } + // // 元に戻すためバックアップ + // var backup = SynchronizationContext.Current; + // // 一時的に変更 + // SynchronizationContext.SetSynchronizationContext(tcs); + // try + // { + // var task = self.LoadAsync(); + + // // 中断された await を消化する + // while (!task.IsCompleted) + // { + // // execute synchronous + // tcs.ExecuteOneCallback(); + // } + // } + // finally + // { + // // 元に戻す + // SynchronizationContext.SetSynchronizationContext(backup); + // } } } } diff --git a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshImporter.cs b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshImporter.cs index 4cc461fdb..a24a99595 100644 --- a/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshImporter.cs +++ b/Assets/UniGLTF/Runtime/UniGLTF/IO/MeshImporter.cs @@ -1,14 +1,15 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; -using System.Threading.Tasks; +using UniGLTF.ExplicitTask; using UnityEngine; namespace UniGLTF { + using Task = ExplicitTask; + public class MeshImporter { const float FRAME_WEIGHT = 100.0f; @@ -653,7 +654,7 @@ namespace UniGLTF return result; } - public static async Task BuildMeshAsync(Func nextFrame, MaterialFactory ctx, MeshImporter.MeshContext meshContext) + public static async ExplicitTask BuildMeshAsync(Func nextFrame, MaterialFactory ctx, MeshImporter.MeshContext meshContext) { var (mesh, recalculateTangents) = _BuildMesh(meshContext); diff --git a/Assets/VRM.Samples/Scripts/VRMRuntimeLoader.cs b/Assets/VRM.Samples/Scripts/VRMRuntimeLoader.cs index 459186ead..d9088a064 100644 --- a/Assets/VRM.Samples/Scripts/VRMRuntimeLoader.cs +++ b/Assets/VRM.Samples/Scripts/VRMRuntimeLoader.cs @@ -1,13 +1,14 @@ #pragma warning disable 0414 -using System; using System.IO; -using System.Runtime.InteropServices; using UniGLTF; +using UniGLTF.ExplicitTask; using UnityEngine; namespace VRM.Samples { + using Task = ExplicitTask; + public class VRMRuntimeLoader : MonoBehaviour { [SerializeField] @@ -102,11 +103,11 @@ namespace VRM.Samples // ParseしたJSONをシーンオブジェクトに変換していく if (m_loadAsync) { - LoadAsync(context); + await LoadAsync(context); } else { - context.Load(); + context.LoadAsync(); OnLoaded(context); } } @@ -164,7 +165,7 @@ namespace VRM.Samples } - async void LoadAsync(VRMImporterContext context) + async Task LoadAsync(VRMImporterContext context) { #if true var now = Time.time; @@ -177,6 +178,7 @@ namespace VRM.Samples // ローカルファイルシステムからロードします VRMImporter.LoadVrmAsync(path, OnLoaded); #endif + return new Unit(); } void LoadBVHClicked() diff --git a/Assets/VRM.Samples/Scripts/ViewerUI.cs b/Assets/VRM.Samples/Scripts/ViewerUI.cs index fa57f118e..57b41164b 100644 --- a/Assets/VRM.Samples/Scripts/ViewerUI.cs +++ b/Assets/VRM.Samples/Scripts/ViewerUI.cs @@ -1,8 +1,8 @@ using System; using System.IO; using System.Linq; -using System.Threading.Tasks; using UniGLTF; +using UniGLTF.ExplicitTask; using UniHumanoid; using UnityEngine; using UnityEngine.UI; @@ -10,6 +10,8 @@ using UnityEngine.UI; namespace VRM.Samples { + using Task = ExplicitTask; + public class ViewerUI : MonoBehaviour { #region UI @@ -105,6 +107,8 @@ namespace VRM.Samples m_textDistributionOther.text = meta.OtherLicenseUrl; m_thumbnail.texture = meta.Thumbnail; + + return new Unit(); } } [SerializeField] diff --git a/Assets/VRM/Runtime/IO/VRMImporterContext.cs b/Assets/VRM/Runtime/IO/VRMImporterContext.cs index 1b1a57f04..10e906d07 100644 --- a/Assets/VRM/Runtime/IO/VRMImporterContext.cs +++ b/Assets/VRM/Runtime/IO/VRMImporterContext.cs @@ -5,10 +5,12 @@ using UniGLTF; using UnityEngine; using System.IO; using UniJSON; -using System.Threading.Tasks; +using UniGLTF.ExplicitTask; namespace VRM { + using Task = ExplicitTask; + public class VRMImporterContext : ImporterContext { public VRM.glTF_VRM_extensions VRM { get; private set; } @@ -84,6 +86,8 @@ namespace VRM { LoadFirstPerson(); } + + return new Unit(); } async Task LoadMetaAsync() @@ -92,6 +96,8 @@ namespace VRM var _meta = Root.AddComponent(); _meta.Meta = meta; Meta = meta; + + return new Unit(); } void LoadFirstPerson() @@ -293,7 +299,7 @@ namespace VRM public BlendShapeAvatar BlendShapeAvatar; public VRMMetaObject Meta; - public async Task ReadMetaAsync(bool createThumbnail = false) + public async ExplicitTask ReadMetaAsync(bool createThumbnail = false) { var meta = ScriptableObject.CreateInstance(); meta.name = "Meta";