using System; using System.Threading.Tasks; namespace VRMShaders { /// /// Runtime (Build 後と、Editor Playing) での非同期ロードを実現する AwaitCaller. /// WebGL など Thread が無いもの向け /// public sealed class RuntimeOnlyNoThreadAwaitCaller : IAwaitCaller { private readonly NextFrameTaskScheduler _scheduler; private readonly float _timeoutInSeconds; private float _lastTimeoutBaseTime; /// /// タイムアウト指定可能なコンストラクタ /// /// NextFrameIfTimedOutがタイムアウトと見なす時間(秒単位) public RuntimeOnlyNoThreadAwaitCaller(float timeoutInSeconds = 1f / 1000f) { _scheduler = new NextFrameTaskScheduler(); _timeoutInSeconds = timeoutInSeconds; ResetLastTimeoutBaseTime(); } public Task NextFrame() { ResetLastTimeoutBaseTime(); var tcs = new TaskCompletionSource(); _scheduler.Enqueue(() => tcs.SetResult(default)); return tcs.Task; } public Task Run(Action action) { try { action(); return Task.FromResult(null); } catch (Exception ex) { return Task.FromException(ex); } } public Task Run(Func action) { try { return Task.FromResult(action()); } catch (Exception ex) { return Task.FromException(ex); } } public Task NextFrameIfTimedOut() => CheckTimeout() ? NextFrame() : Task.CompletedTask; private void ResetLastTimeoutBaseTime() => _lastTimeoutBaseTime = 0f; private bool LastTimeoutBaseTimeNeedsReset => _lastTimeoutBaseTime == 0f; private bool CheckTimeout() { float t = UnityEngine.Time.realtimeSinceStartup; if (LastTimeoutBaseTimeNeedsReset) { _lastTimeoutBaseTime = t; } return (t - _lastTimeoutBaseTime) >= _timeoutInSeconds; } } }