您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

730 行
26 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using developer;
using Unity.UIWidgets.async;
using Unity.UIWidgets.async;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.painting;
using Unity.UIWidgets.ui;
using UnityEngine;
using FrameTiming = Unity.UIWidgets.ui.FrameTiming;
namespace Unity.UIWidgets.scheduler {
public static partial class scheduler_ {
static float _timeDilation = 1.0f;
public static float timeDilation {
get { return _timeDilation; }
set {
D.assert(value > 0.0f);
if (_timeDilation == value) {
return;
}
SchedulerBinding.instance?.resetEpoch();
_timeDilation = value;
}
}
}
public delegate void FrameCallback(TimeSpan timeStamp);
public delegate T TaskCallback<out T>();
public delegate bool SchedulingStrategy(int priority = 0, SchedulerBinding scheduler = null);
interface _TaskEntry : IComparable<_TaskEntry> {
int priority { get; }
string debugStack { get; }
void run();
}
class _TaskEntry<T> : _TaskEntry {
public readonly TaskCallback<T> task;
public Completer completer;
internal _TaskEntry(TaskCallback<T> task, int priority) {
this.task = task;
this.priority = priority;
D.assert(() => {
debugStack = StackTraceUtility.ExtractStackTrace();
return true;
});
completer = Completer.create();
}
public int priority { get; }
public string debugStack { get; private set; }
public void run() {
if (!foundation_.kReleaseMode) {
completer.complete(FutureOr.value(task()));
}
else {
completer.complete(FutureOr.value(task()));
}
}
public int CompareTo(_TaskEntry other) {
return -priority.CompareTo(value: other.priority);
}
}
class _FrameCallbackEntry {
public static string debugCurrentCallbackStack;
public readonly FrameCallback callback;
public string debugStack;
internal _FrameCallbackEntry(FrameCallback callback, bool rescheduling = false) {
this.callback = callback;
D.assert(() => {
if (rescheduling) {
D.assert(() => {
if (debugCurrentCallbackStack == null) {
throw new UIWidgetsError(
new List<DiagnosticsNode> {
new ErrorSummary(
"scheduleFrameCallback called with rescheduling true, but no callback is in scope."),
new ErrorDescription(
"The \"rescheduling\" argument should only be set to true if the " +
"callback is being reregistered from within the callback itself, " +
"and only then if the callback itself is entirely synchronous."),
new ErrorHint("If this is the initial registration of the callback, or if the " +
"callback is asynchronous, then do not use the \"rescheduling\" " +
"argument.")
});
}
return true;
});
debugStack = debugCurrentCallbackStack;
}
else {
debugStack = StackTraceUtility.ExtractStackTrace();
}
return true;
});
}
}
public enum SchedulerPhase {
idle,
transientCallbacks,
midFrameMicrotasks,
persistentCallbacks,
postFrameCallbacks
}
public class SchedulerBinding : PaintingBinding {
readonly List<FrameCallback> _persistentCallbacks = new List<FrameCallback>();
readonly List<FrameCallback> _postFrameCallbacks = new List<FrameCallback>();
readonly HashSet<int> _removedIds = new HashSet<int>();
readonly PriorityQueue<_TaskEntry> _taskQueue = new PriorityQueue<_TaskEntry>();
readonly List<TimingsCallback> _timingsCallbacks = new List<TimingsCallback>();
TimeSpan? _currentFrameTimeStamp;
string _debugBanner;
int _debugFrameNumber;
TimeSpan _epochStart = TimeSpan.Zero;
TimeSpan? _firstRawTimeStampInEpoch;
bool _hasRequestedAnEventLoopCallback;
bool _ignoreNextEngineDrawFrame;
int _nextFrameCallbackId;
Completer _nextFrameCompleter;
Dictionary<int, _FrameCallbackEntry> _transientCallbacks = new Dictionary<int, _FrameCallbackEntry>();
bool _warmUpFrame;
public SchedulingStrategy schedulingStrategy = scheduler_.defaultSchedulingStrategy;
public static SchedulerBinding instance {
get { return (SchedulerBinding) Window.instance._binding; }
private set { Window.instance._binding = value; }
}
public AppLifecycleState? lifecycleState { get; private set; }
public int transientCallbackCount {
get { return _transientCallbacks.Count; }
}
public Future endOfFrame {
get {
if (_nextFrameCompleter == null) {
if (schedulerPhase == SchedulerPhase.idle) {
scheduleFrame();
}
_nextFrameCompleter = Completer.create();
addPostFrameCallback(timeStamp => {
_nextFrameCompleter.complete();
_nextFrameCompleter = null;
});
}
return _nextFrameCompleter.future;
}
}
public bool hasScheduledFrame { get; private set; }
public SchedulerPhase schedulerPhase { get; private set; } = SchedulerPhase.idle;
public bool framesEnabled { get; private set; } = true;
public TimeSpan currentFrameTimeStamp {
get {
D.assert(_currentFrameTimeStamp != null);
return _currentFrameTimeStamp.Value;
}
}
public TimeSpan currentSystemFrameTimeStamp { get; private set; } = TimeSpan.Zero;
protected override void initInstances() {
base.initInstances();
instance = this;
//SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
if (!foundation_.kReleaseMode) {
var frameNumber = 0;
addTimingsCallback(timings => {
foreach (var frameTiming in timings) {
frameNumber += 1;
_profileFramePostEvent(frameNumber: frameNumber, frameTiming: frameTiming);
}
});
}
}
public void addTimingsCallback(TimingsCallback callback) {
_timingsCallbacks.Add(item: callback);
if (_timingsCallbacks.Count == 1) {
D.assert(window.onReportTimings == null);
window.onReportTimings = _executeTimingsCallbacks;
}
D.assert(window.onReportTimings == _executeTimingsCallbacks);
}
public void removeTimingsCallback(TimingsCallback callback) {
D.assert(_timingsCallbacks.Contains(item: callback));
_timingsCallbacks.Remove(item: callback);
if (_timingsCallbacks.isEmpty()) {
window.onReportTimings = null;
}
}
void _executeTimingsCallbacks(List<FrameTiming> timings) {
var clonedCallbacks =
new List<TimingsCallback>(collection: _timingsCallbacks);
foreach (var callback in clonedCallbacks) {
try {
if (_timingsCallbacks.Contains(item: callback)) {
callback(timings: timings);
}
}
catch (Exception ex) {
InformationCollector collector = null;
D.assert(() => {
IEnumerable<DiagnosticsNode> infoCollect() {
yield return new DiagnosticsProperty<TimingsCallback>(
"The TimingsCallback that gets executed was",
value: callback,
style: DiagnosticsTreeStyle.errorProperty);
}
collector = infoCollect;
return true;
});
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: ex,
context: new ErrorDescription("while executing callbacks for FrameTiming"),
informationCollector: collector
));
}
}
}
protected void readInitialLifecycleStateFromNativeWindow() {
if (lifecycleState == null) {
handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message: window.initialLifecycleState));
}
}
protected virtual void handleAppLifecycleStateChanged(AppLifecycleState state) {
lifecycleState = state;
switch (state) {
case AppLifecycleState.resumed:
case AppLifecycleState.inactive:
_setFramesEnabledState(true);
break;
case AppLifecycleState.paused:
case AppLifecycleState.detached:
_setFramesEnabledState(false);
break;
}
}
static AppLifecycleState _parseAppLifecycleMessage(string message) {
switch (message) {
case "AppLifecycleState.paused":
return AppLifecycleState.paused;
case "AppLifecycleState.resumed":
return AppLifecycleState.resumed;
case "AppLifecycleState.inactive":
return AppLifecycleState.inactive;
case "AppLifecycleState.detached":
return AppLifecycleState.detached;
}
throw new Exception("unknown AppLifecycleState: " + message);
}
public Future scheduleTask<T>(
TaskCallback<T> task,
Priority priority) {
var isFirstTask = _taskQueue.isEmpty;
var entry = new _TaskEntry<T>(
task: task,
priority: priority.value
);
_taskQueue.enqueue(item: entry);
if (isFirstTask && !locked) {
_ensureEventLoopCallback();
}
return entry.completer.future;
}
protected override void unlocked() {
base.unlocked();
if (_taskQueue.isNotEmpty) {
_ensureEventLoopCallback();
}
}
void _ensureEventLoopCallback() {
D.assert(result: !locked);
D.assert(_taskQueue.count != 0);
if (_hasRequestedAnEventLoopCallback) {
return;
}
_hasRequestedAnEventLoopCallback = true;
Timer.run(callback: _runTasks);
}
object _runTasks() {
_hasRequestedAnEventLoopCallback = false;
if (handleEventLoopCallback()) {
_ensureEventLoopCallback(); // runs next task when there's time
}
return null;
}
bool handleEventLoopCallback() {
if (_taskQueue.isEmpty || locked) {
return false;
}
var entry = _taskQueue.first;
if (schedulingStrategy(priority: entry.priority, this)) {
try {
_taskQueue.removeFirst();
entry.run();
}
catch (Exception exception) {
string callbackStack = null;
D.assert(() => {
callbackStack = entry.debugStack;
return true;
});
IEnumerable<DiagnosticsNode> infoCollector() {
yield return DiagnosticsNode.message(
"\nThis exception was thrown in the context of a scheduler callback. " +
"When the scheduler callback was _registered_ (as opposed to when the " +
"exception was thrown), this was the stack: " + callbackStack);
}
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: exception,
"scheduler library",
new ErrorDescription("during a task callback"),
informationCollector: callbackStack == null
? (InformationCollector) null
: infoCollector
));
}
return _taskQueue.isNotEmpty;
}
return false;
}
public int scheduleFrameCallback(FrameCallback callback, bool rescheduling = false) {
scheduleFrame();
_nextFrameCallbackId += 1;
_transientCallbacks[key: _nextFrameCallbackId] =
new _FrameCallbackEntry(callback: callback, rescheduling: rescheduling);
return _nextFrameCallbackId;
}
public void cancelFrameCallbackWithId(int id) {
D.assert(id > 0);
_transientCallbacks.Remove(key: id);
_removedIds.Add(item: id);
}
public void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.Add(item: callback);
}
public void addPostFrameCallback(FrameCallback callback) {
_postFrameCallbacks.Add(item: callback);
}
void _setFramesEnabledState(bool enabled) {
if (framesEnabled == enabled) {
return;
}
framesEnabled = enabled;
if (enabled) {
scheduleFrame();
}
}
protected void ensureFrameCallbacksRegistered() {
window.onBeginFrame = window.onBeginFrame ?? _handleBeginFrame;
window.onDrawFrame = window.onDrawFrame ?? _handleDrawFrame;
}
public void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
public void scheduleFrame() {
if (hasScheduledFrame || !framesEnabled) {
return;
}
D.assert(() => {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleFrame() called. Current phase is {0}.", schedulerPhase);
}
return true;
});
ensureFrameCallbacksRegistered();
Window.instance.scheduleFrame();
hasScheduledFrame = true;
}
public void scheduleForcedFrame() {
if (!framesEnabled) {
return;
}
if (hasScheduledFrame) {
return;
}
D.assert(() => {
if (scheduler_.debugPrintScheduleFrameStacks) {
Debug.LogFormat("scheduleForcedFrame() called. Current phase is {0}.", schedulerPhase);
}
return true;
});
ensureFrameCallbacksRegistered();
Window.instance.scheduleFrame();
hasScheduledFrame = true;
}
public void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) {
return;
}
_warmUpFrame = true;
Timeline.startSync("Warm-up frame");
var hadScheduledFrame = hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() => {
D.assert(result: _warmUpFrame);
handleBeginFrame(null);
return null;
});
Timer.run(() => {
D.assert(result: _warmUpFrame);
handleDrawFrame();
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame's timestamp will typically be far in
// the past (the time of the last real frame), so if we didn't reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the "real" frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame) {
scheduleFrame();
}
return null;
});
// Lock events so touch events etc don't insert themselves until the
// scheduled frame has finished.
lockEvents(() => endOfFrame.then(v => {
Timeline.finishSync();
return FutureOr.nil;
}));
}
public void resetEpoch() {
_epochStart = _adjustForEpoch(rawTimeStamp: currentSystemFrameTimeStamp);
_firstRawTimeStampInEpoch = null;
}
TimeSpan _adjustForEpoch(TimeSpan rawTimeStamp) {
var rawDurationSinceEpoch = _firstRawTimeStampInEpoch == null
? TimeSpan.Zero
: rawTimeStamp - _firstRawTimeStampInEpoch.Value;
return new TimeSpan((long) (rawDurationSinceEpoch.Ticks / scheduler_.timeDilation) +
_epochStart.Ticks);
}
void _handleBeginFrame(TimeSpan rawTimeStamp) {
if (_warmUpFrame) {
D.assert(result: !_ignoreNextEngineDrawFrame);
_ignoreNextEngineDrawFrame = true;
return;
}
handleBeginFrame(rawTimeStamp: rawTimeStamp);
}
void _handleDrawFrame() {
if (_ignoreNextEngineDrawFrame) {
_ignoreNextEngineDrawFrame = false;
return;
}
handleDrawFrame();
}
public void handleBeginFrame(TimeSpan? rawTimeStamp) {
Timeline.startSync("Frame");
_firstRawTimeStampInEpoch = _firstRawTimeStampInEpoch ?? rawTimeStamp;
_currentFrameTimeStamp = _adjustForEpoch(rawTimeStamp ?? currentSystemFrameTimeStamp);
if (rawTimeStamp != null) {
currentSystemFrameTimeStamp = rawTimeStamp.Value;
}
D.assert(() => {
_debugFrameNumber += 1;
if (scheduler_.debugPrintBeginFrameBanner || scheduler_.debugPrintEndFrameBanner) {
var frameTimeStampDescription = new StringBuilder();
if (rawTimeStamp != null) {
_debugDescribeTimeStamp(timeStamp: _currentFrameTimeStamp.Value,
buffer: frameTimeStampDescription);
}
else {
frameTimeStampDescription.Append("(warm-up frame)");
}
_debugBanner =
$"▄▄▄▄▄▄▄▄ Frame {_debugFrameNumber.ToString().PadRight(7)} ${frameTimeStampDescription.ToString().PadLeft(18)} ▄▄▄▄▄▄▄▄";
if (scheduler_.debugPrintBeginFrameBanner) {
Debug.Log(message: _debugBanner);
}
}
return true;
});
D.assert(schedulerPhase == SchedulerPhase.idle);
hasScheduledFrame = false;
try {
Timeline.startSync("Animate");
schedulerPhase = SchedulerPhase.transientCallbacks;
var callbacks = _transientCallbacks;
_transientCallbacks = new Dictionary<int, _FrameCallbackEntry>();
foreach (var entry in callbacks) {
if (!_removedIds.Contains(item: entry.Key)) {
_invokeFrameCallback(
callback: entry.Value.callback, timeStamp: _currentFrameTimeStamp.Value,
callbackStack: entry.Value.debugStack);
}
}
_removedIds.Clear();
}
finally {
schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
public void handleDrawFrame() {
D.assert(schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync();
try {
schedulerPhase = SchedulerPhase.persistentCallbacks;
foreach (var callback in _persistentCallbacks) {
_invokeFrameCallback(callback: callback, timeStamp: _currentFrameTimeStamp.Value);
}
schedulerPhase = SchedulerPhase.postFrameCallbacks;
var localPostFrameCallbacks = new List<FrameCallback>(collection: _postFrameCallbacks);
_postFrameCallbacks.Clear();
foreach (var callback in localPostFrameCallbacks) {
_invokeFrameCallback(callback: callback, timeStamp: _currentFrameTimeStamp.Value);
}
}
finally {
schedulerPhase = SchedulerPhase.idle;
D.assert(() => {
if (scheduler_.debugPrintEndFrameBanner) {
Debug.Log(new string('▀', count: _debugBanner.Length));
}
_debugBanner = null;
return true;
});
_currentFrameTimeStamp = null;
}
}
void _profileFramePostEvent(int frameNumber, FrameTiming frameTiming) {
developer_.postEvent("Flutter.Frame", new Hashtable {
{"number", frameNumber},
{"startTime", frameTiming.timestampInMicroseconds(phase: FramePhase.buildStart)},
{"elapsed", (int) (frameTiming.totalSpan.TotalMilliseconds * 1000)},
{"build", (int) (frameTiming.buildDuration.TotalMilliseconds * 1000)},
{"raster", (int) (frameTiming.rasterDuration.TotalMilliseconds * 1000)}
});
}
static void _debugDescribeTimeStamp(TimeSpan timeStamp, StringBuilder buffer) {
if (timeStamp.TotalDays > 0) {
buffer.AppendFormat("{0}d ", arg0: timeStamp.Days);
}
if (timeStamp.TotalHours > 0) {
buffer.AppendFormat("{0}h ", arg0: timeStamp.Hours);
}
if (timeStamp.TotalMinutes > 0) {
buffer.AppendFormat("{0}m ", arg0: timeStamp.Minutes);
}
if (timeStamp.TotalSeconds > 0) {
buffer.AppendFormat("{0}s ", arg0: timeStamp.Seconds);
}
buffer.AppendFormat("{0}", arg0: timeStamp.Milliseconds);
var microseconds = (int) (timeStamp.Ticks % 10000 / 10);
if (microseconds > 0) {
buffer.AppendFormat(".{0}", microseconds.ToString().PadLeft(3, '0'));
}
buffer.Append("ms");
}
void _invokeFrameCallback(FrameCallback callback, TimeSpan timeStamp, string callbackStack = null) {
D.assert(callback != null);
D.assert(_FrameCallbackEntry.debugCurrentCallbackStack == null);
D.assert(() => {
_FrameCallbackEntry.debugCurrentCallbackStack = callbackStack;
return true;
});
try {
callback(timeStamp: timeStamp);
}
catch (Exception ex) {
IEnumerable<DiagnosticsNode> infoCollector() {
yield return DiagnosticsNode.message(
"\nThis exception was thrown in the context of a scheduler callback. " +
"When the scheduler callback was _registered_ (as opposed to when the " +
"exception was thrown), this was the stack:");
var builder = new StringBuilder();
foreach (var line in UIWidgetsError.defaultStackFilter(
callbackStack.TrimEnd().Split('\n'))) {
builder.AppendLine(value: line);
}
yield return DiagnosticsNode.message(builder.ToString());
}
UIWidgetsError.reportError(new UIWidgetsErrorDetails(
exception: ex,
"scheduler library",
new ErrorDescription("during a scheduler callback"),
informationCollector: callbackStack == null
? (InformationCollector) null
: infoCollector
));
}
D.assert(() => {
_FrameCallbackEntry.debugCurrentCallbackStack = null;
return true;
});
}
}
public static partial class scheduler_ {
public static bool defaultSchedulingStrategy(int priority, SchedulerBinding scheduler) {
if (scheduler.transientCallbackCount > 0) {
return priority >= Priority.animation.value;
}
return true;
}
}
}