< Summary

Class:DCL.SkipFrameIfDepletedTimeBudget
Assembly:CoroutineHelpers
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Helpers/CoroutineHelpers/DCLCoroutineRunner.cs
Covered lines:0
Uncovered lines:1
Coverable lines:1
Total lines:127
Line coverage:0% (0 of 1)
Covered branches:0
Total branches:0

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Helpers/CoroutineHelpers/DCLCoroutineRunner.cs

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using UnityEngine;
 5
 6namespace DCL
 7{
 8    /// <summary>
 9    /// Based on work from: http://JacksonDunstan.com/articles/3718
 10    /// </summary>
 11    public static class DCLCoroutineRunner
 12    {
 13        public static Func<float> realtimeSinceStartup = () => Time.realtimeSinceStartup;
 14
 15        /// <summary>
 16        /// Run an iterator function that might throw an exception. Call the callback with the exception
 17        /// if it does or null if it finishes without throwing an exception.
 18        /// </summary>
 19        /// <param name="enumerator">Iterator function to run</param>
 20        /// <param name="onException">Callback to call when the iterator has thrown an exception or finished.
 21        /// The thrown exception or null is passed as the parameter.</param>
 22        /// <param name="timeBudgetCounter">A func that takes elapsed time as parameter, and returns a bool
 23        /// indicating if a frame should be skipped or not. Use this in combination with ThrottlingCounter.EvaluateTimeB
 24        /// If this is passed as null, no time budget will be used.</param>
 25        /// <returns>An enumerator that runs the given enumerator</returns>
 26        public static IEnumerator Run(
 27            IEnumerator enumerator,
 28            Action<Exception> onException,
 29            Func<double, bool> timeBudgetCounter = null
 30        )
 31        {
 32            float currentTime = realtimeSinceStartup();
 33            // The enumerator might yield return enumerators, in which case
 34            // we need to enumerate those here rather than yield-returning
 35            // them. Otherwise, any exceptions thrown by those "inner enumerators"
 36            // would actually escape to an outer level of iteration, outside this
 37            // code here, and not be passed to the done callback.
 38            // So, this stack holds any inner enumerators.
 39            var stack = new Stack<IEnumerator>();
 40            stack.Push(enumerator);
 41
 42            while (stack.Count > 0)
 43            {
 44                // any inner enumerator will be at the top of the stack
 45                // otherwise the original one
 46                var currentEnumerator = stack.Peek();
 47                // this is what get "yield returned" in the work enumerator
 48                object currentYieldedObject;
 49                // the contents of this try block run the work enumerator until
 50                // it gets to a yield return statement
 51                try
 52                {
 53                    if (currentEnumerator.MoveNext() == false)
 54                    {
 55                        // in this case, the enumerator has finished
 56                        stack.Pop();
 57                        // if the stack is empty, then everything has finished,
 58                        // and the while (stack.Count &gt; 0) will pick it up
 59                        continue;
 60                    }
 61
 62                    currentYieldedObject = currentEnumerator.Current;
 63                }
 64                catch (Exception ex)
 65                {
 66                    // this part is the whole point of this method!
 67                    onException(ex);
 68                    yield break;
 69                }
 70
 71                float newCurrentTime = realtimeSinceStartup();
 72                float elapsedTime = newCurrentTime - currentTime;
 73                currentTime = newCurrentTime;
 74
 75                // NOTE: SkipFrameIfDepletedTimeBudget object type is used as a special token here and will not
 76                // yield unless the time budget is exceeded for this frame.
 77                //
 78                // Handling the time budget frame skip for yield return null; calls was also considered.
 79                // But this means that yield return null; will no longer skip a frame unless the time budget
 80                // is exceeded. If the user wanted to skip the frame explicitly this detail would change
 81                // the intended behaviour and introduce bugs.
 82                bool handleTimeBudget = timeBudgetCounter != null
 83                                        && currentYieldedObject != null
 84                                        && currentYieldedObject is SkipFrameIfDepletedTimeBudget;
 85
 86                if ( handleTimeBudget )
 87                {
 88                    if ( timeBudgetCounter( elapsedTime ) )
 89                    {
 90                        yield return null;
 91                        currentTime = realtimeSinceStartup();
 92                    }
 93
 94                    continue;
 95                }
 96
 97                // in unity you can yield return whatever the hell you want,
 98                // so this will pick up whether it's something to enumerate
 99                // here, or pass through by yield returning it
 100                if (currentYieldedObject is IEnumerator)
 101                {
 102                    stack.Push(currentYieldedObject as IEnumerator);
 103                }
 104                else
 105                {
 106                    yield return currentYieldedObject;
 107                    currentTime = realtimeSinceStartup();
 108
 109                    // Force reset of time budget if a frame is skipped on purpose
 110                    if ( timeBudgetCounter != null )
 111                        timeBudgetCounter( double.MaxValue );
 112                }
 113            }
 114        }
 115    }
 116
 117    /// <summary>
 118    /// When a coroutine is started with throttling, yielding this object
 119    /// will make the frame skip ONLY if the time budget is exceeded.
 120    ///
 121    /// If the time budget is not exceeded, no frames will be skipped by yielding this object.
 122    /// </summary>
 123    public class SkipFrameIfDepletedTimeBudget : CustomYieldInstruction
 124    {
 0125        public override bool keepWaiting => false;
 126    }
 127}

Methods/Properties

keepWaiting()