/// <summary> /// Run this as a coroutine to do heavy work across multiple frames, if it takes longer than the frame time budget. /// </summary> /// <typeparam name="TResults">A combination of all TSingleResult values.</typeparam> /// <typeparam name="TSingleResult">A single piece of the TResults value.</typeparam> /// <param name="initialResults">Initial value of the results container.</param> /// <param name="sequence">The generator sequence that performs the heavy work that produces TSingleResult.</param> /// <param name="processSingleResultFunc">A delegate that processes a TSingleResult value and combines it with the TResults value. Called once per TSingleResult produced.</param> /// <param name="processAccumulatedResultsFunc">A delegate that processes the partially completed TResults value. Called each time the coroutine yields when it exceeds the frame time budget.</param> /// <param name="processResultsFunc">A delegate that processes the completed TResults value. Called once when the work completes.</param> /// <param name="frameTimeBudget">How many milliseconds to allow the generator sequence to run for each frame.</param> /// <returns>A IEnumerator to be run as a Unity coroutine.</returns> public static IEnumerator FrameTimeBudgettedCoroutine <TResults, TSingleResult>(TResults initialResults, IEnumerable <TSingleResult> sequence, ProcessSingleResultDelegate <TResults, TSingleResult> processSingleResultFunc, ProcessAccumulatedResultsDelegate <TResults> processAccumulatedResultsFunc, ProcessResultsDelegate <TResults> processResultsFunc, double frameTimeBudget) { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); double lastElapsed = 0L; TResults results = initialResults; IEnumerator <TSingleResult> enumerator = sequence.GetEnumerator(); // Calculate next until sequence ends while (enumerator.MoveNext()) { // Time spent in this frame double frameTimeSpent = stopwatch.Elapsed.TotalMilliseconds - lastElapsed; // If exceeded frame time budget if (frameTimeSpent >= frameTimeBudget) { // Invoke callback for handling partial results if (processAccumulatedResultsFunc != null) { processAccumulatedResultsFunc(ref results); } // Wait for next frame yield return(null); // Remember when we finished waiting for next frame lastElapsed = stopwatch.ElapsedMilliseconds; } TSingleResult singleResult = enumerator.Current; // Invoke callback for handling single result if (processSingleResultFunc != null) { processSingleResultFunc(ref results, ref singleResult); } } stopwatch.Stop(); // Invoke callback for handling completed results if (processResultsFunc != null) { processResultsFunc(ref results); } }
/// <summary> /// Run this as a coroutine to do heavy work across multiple frames, if it takes longer than the frame time budget. /// </summary> /// <typeparam name="TResults">A combination of all TSingleResult values.</typeparam> /// <typeparam name="TSingleResult">A single piece of the TResults value.</typeparam> /// <param name="initialResults">Initial value of the results container.</param> /// <param name="sequence">The generator sequence that performs the heavy work that produces TSingleResult.</param> /// <param name="processSingleResultFunc">A delegate that processes a TSingleResult value and combines it with the TResults value. Called once per TSingleResult produced.</param> /// <param name="processResultsFunc">A delegate that processes the completed TResults value. Called once when the work completes.</param> /// <param name="frameTimeBudget">How many milliseconds to allow the generator sequence to run for each frame.</param> /// <returns>A IEnumerator to be run as a Unity coroutine.</returns> public static IEnumerator FrameTimeBudgettedCoroutine <TResults, TSingleResult>(TResults initialResults, IEnumerable <TSingleResult> sequence, ProcessSingleResultDelegate <TResults, TSingleResult> processSingleResultFunc, ProcessResultsDelegate <TResults> processResultsFunc, double frameTimeBudget) { return(FrameTimeBudgettedCoroutine(initialResults, sequence, processSingleResultFunc, null, processResultsFunc, frameTimeBudget)); }