/// <summary> /// Deactivates and disables this root clock. /// </summary> internal void RootDisable() { Debug.Assert(IsTimeManager, "Invalid call to RootDeactivate for a non-root Clock"); // Reset the state of the timing tree WeakRefEnumerator <Clock> enumerator = new WeakRefEnumerator <Clock>(_rootChildren); while (enumerator.MoveNext()) { PrefixSubtreeEnumerator subtree = new PrefixSubtreeEnumerator(enumerator.Current, true); while (subtree.MoveNext()) { if (subtree.Current.InternalCurrentClockState != ClockState.Stopped) { subtree.Current.ResetCachedStateToStopped(); subtree.Current.RaiseCurrentStateInvalidated(); subtree.Current.RaiseCurrentTimeInvalidated(); subtree.Current.RaiseCurrentGlobalSpeedInvalidated(); } else { subtree.SkipSubtree(); } } } }
// Called on the root internal void ComputeTreeState() { Debug.Assert(IsTimeManager); // Revive all children WeakRefEnumerator <Clock> enumerator = new WeakRefEnumerator <Clock>(_rootChildren); while (enumerator.MoveNext()) { PrefixSubtreeEnumerator prefixEnumerator = new PrefixSubtreeEnumerator(enumerator.Current, true); while (prefixEnumerator.MoveNext()) { Clock current = prefixEnumerator.Current; // Only traverse the "ripe" subset of the Timing tree if (CurrentGlobalTime >= current.InternalNextTickNeededTime) { current.ApplyDesiredFrameRateToGlobalTime(); current.ComputeLocalState(); // Compute the state of the node current.ClipNextTickByParent(); // Perform NextTick clipping, stage 1 // Make a note to visit for stage 2, only for ClockGroups and Roots current.NeedsPostfixTraversal = (current is ClockGroup) || (current.IsRoot); } else { prefixEnumerator.SkipSubtree(); } } } // To perform a postfix walk culled by NeedsPostfixTraversal flag, we use a local recursive method // Note that since we called for this operation, it is probably already needed by the root clock ComputeTreeStateRoot(); }
/// <summary> /// The only things that can change for this are the begin time of this /// timeline /// </summary> /// <param name="destination"> /// The destination to seek to, relative to the clock's BeginTime. If this is past the /// active perioed execute the FillBehavior. /// </param> internal void InternalSeekAlignedToLastTick(TimeSpan destination) { Debug.Assert(IsRoot); // This is a no-op with a null TimeManager or when all durations have not yet been resolved if (_timeManager == null || HasDescendantsWithUnresolvedDuration) { return; } // Adjust _beginTime such that our current time equals the Seek position // that was requested _beginTime = CurrentGlobalTime - DivideTimeSpan(destination, _appliedSpeedRatio); if (CanGrow) { _currentIteration = null; // This node is not visited by ResetSlipOnSubtree _currentIterationBeginTime = _beginTime; ResetSlipOnSubtree(); UpdateSyncBeginTime(); } IsInteractivelyStopped = false; // We have unset disabled status PendingInteractiveStop = false; RootBeginPending = false; // Cancel a pending begin ResetNodesWithSlip(); // Reset [....] tracking _timeManager.InternalCurrentIntervals = TimeIntervalCollection.Empty; PrefixSubtreeEnumerator subtree = new PrefixSubtreeEnumerator(this, true); while (subtree.MoveNext()) { // We are processing a Seek immediately. We don't need a TIC yet // since we are not computing events, and we don't want to // process pending stuff either. subtree.Current.ComputeLocalStateHelper(false, true); // Compute the state of the node if (HasDiscontinuousTimeMovementOccured) { DiscontinuousTimeMovement(); HasDiscontinuousTimeMovementOccured = false; } subtree.Current.ClipNextTickByParent(); // Perform NextTick clipping, stage 1 // Make a note to visit for stage 2, only for ClockGroups subtree.Current.NeedsPostfixTraversal = (subtree.Current is ClockGroup); } _parent.ComputeTreeStateRoot(); // Re-clip the next tick estimates by children // Fire the events indicating that we've invalidated this whole subtree subtree.Reset(); while (subtree.MoveNext()) { // CurrentTimeInvalidated should be fired first to give AnimationStorage a chance // to update the value. We directly set the flags, then fire RaiseAccumulatedEvents // to avoid involving the TimeManager for this local subtree operation. subtree.Current.CurrentTimeInvalidatedEventRaised = true; subtree.Current.CurrentStateInvalidatedEventRaised = true; subtree.Current.CurrentGlobalSpeedInvalidatedEventRaised = true; subtree.Current.RaiseAccumulatedEvents(); } }
// // Private Methods // #region Private Methods // // Local State Computation Helpers // #region Local State Computation Helpers // // Seek, Begin and Pause are internally implemented by adjusting the begin time. // For example, when paused, each tick moves the begin time forward so that // overall the clock hasn't moved. // // Note that _beginTime is an offset from the parent's begin. Since // these are root clocks, the parent is the TimeManager, and we // must add in the CurrentGlobalTime private void AdjustBeginTime() { Debug.Assert(IsRoot); // root clocks only; non-roots have constant begin time Debug.Assert(_rootData != null); // Process the effects of Seek, Begin, and Pause; delay request if not all durations are resolved in this subtree. if (_rootData.PendingSeekDestination.HasValue && !HasDescendantsWithUnresolvedDuration) { Debug.Assert(!RootBeginPending); // we can have either a begin or a seek, not both // Adjust the begin time such that our current time equals PendingSeekDestination _beginTime = CurrentGlobalTime - DivideTimeSpan(_rootData.PendingSeekDestination.Value, _appliedSpeedRatio); if (CanGrow) // One of our descendants has set this flag on us { _currentIterationBeginTime = _beginTime; // We relied on a combination of _currentIterationBeginTime and _currentIteration for our state _currentIteration = null; // Therefore, we should reset both to reset our position ResetSlipOnSubtree(); } UpdateSyncBeginTime(); _rootData.PendingSeekDestination = null; // We have seeked, so raise all events signifying that no assumptions can be made about our state PrefixSubtreeEnumerator subtree = new PrefixSubtreeEnumerator(this, true); while (subtree.MoveNext()) { subtree.Current.RaiseCurrentStateInvalidated(); subtree.Current.RaiseCurrentTimeInvalidated(); subtree.Current.RaiseCurrentGlobalSpeedInvalidated(); } } else if (RootBeginPending) { // RootBeginPending is set when a root is parented to a tree (in AddToRoot()). // It allows us to interpret Timeline.BeginTime as an offset from the current // time and thus schedule a begin in the future. _beginTime = CurrentGlobalTime + _timeline.BeginTime; if (CanGrow) // One of our descendants has set this flag on us { _currentIterationBeginTime = _beginTime; // We should be just starting our first iteration now } UpdateSyncBeginTime(); RootBeginPending = false; } else if ((IsInteractivelyPaused || _rootData.InteractiveSpeedRatio == 0) && (_syncData == null || !_syncData.IsInSyncPeriod)) // We were paused at the last tick, so move _beginTime by the delta from last tick to this one // Only perform this iff we are *continuously* moving, e.g. if we haven't seeked between ticks. // [....] NOTE: If we are syncing, then the [....] code should be the one to make this adjustment // by using the Media's current time (which should already be paused). { if (_beginTime.HasValue) { // Adjust for the speed of this timelineClock _beginTime += _timeManager.LastTickDelta; UpdateSyncBeginTime(); if (_currentIterationBeginTime.HasValue) // One of our descendants has set this flag on us { _currentIterationBeginTime += _timeManager.LastTickDelta; } } } // Adjust for changes to the speed ratio if (_rootData.PendingSpeedRatio.HasValue) { double pendingSpeedRatio = _rootData.PendingSpeedRatio.Value * _timeline.SpeedRatio; // If the calculated speed ratio is 0, we reset it to 1. I believe this is // because we don't want to support pausing by setting speed ratio. Instead // they should call pause. if (pendingSpeedRatio == 0) { pendingSpeedRatio = 1; } Debug.Assert(_beginTime.HasValue); // Below code uses the above assumption that beginTime has a value TimeSpan previewParentTime = CurrentGlobalTime; if (_currentIterationBeginTime.HasValue) { // Adjusting SpeedRatio is not a discontiuous event, we don't want to reset slip after doing this _currentIterationBeginTime = previewParentTime - MultiplyTimeSpan(previewParentTime - _currentIterationBeginTime.Value, _appliedSpeedRatio / pendingSpeedRatio); } else { _beginTime = previewParentTime - MultiplyTimeSpan(previewParentTime - _beginTime.Value, _appliedSpeedRatio / pendingSpeedRatio); } RaiseCurrentGlobalSpeedInvalidated(); // _appliedSpeedRatio represents the speed ratio we're actually using // for this Clock. _appliedSpeedRatio = pendingSpeedRatio; // _rootData.InteractiveSpeedRatio represents the actual user set interactive // speed ratio value even though we may override it if it's 0. _rootData.InteractiveSpeedRatio = _rootData.PendingSpeedRatio.Value; // Clear out the new pending speed ratio since we've finished applying it. _rootData.PendingSpeedRatio = null; UpdateSyncBeginTime(); } return; }
/// <summary> /// Deactivates and disables this root clock. /// </summary> internal void RootDisable() { Debug.Assert(IsTimeManager, "Invalid call to RootDeactivate for a non-root Clock"); // Reset the state of the timing tree WeakRefEnumerator<Clock> enumerator = new WeakRefEnumerator<Clock>(_rootChildren); while (enumerator.MoveNext()) { PrefixSubtreeEnumerator subtree = new PrefixSubtreeEnumerator(enumerator.Current, true); while (subtree.MoveNext()) { if (subtree.Current.InternalCurrentClockState != ClockState.Stopped) { subtree.Current.ResetCachedStateToStopped(); subtree.Current.RaiseCurrentStateInvalidated(); subtree.Current.RaiseCurrentTimeInvalidated(); subtree.Current.RaiseCurrentGlobalSpeedInvalidated(); } else { subtree.SkipSubtree(); } } } }
// Called on the root internal void ComputeTreeState() { Debug.Assert(IsTimeManager); // Revive all children WeakRefEnumerator<Clock> enumerator = new WeakRefEnumerator<Clock>(_rootChildren); while (enumerator.MoveNext()) { PrefixSubtreeEnumerator prefixEnumerator = new PrefixSubtreeEnumerator(enumerator.Current, true); while (prefixEnumerator.MoveNext()) { Clock current = prefixEnumerator.Current; // Only traverse the "ripe" subset of the Timing tree if (CurrentGlobalTime >= current.InternalNextTickNeededTime) { current.ApplyDesiredFrameRateToGlobalTime(); current.ComputeLocalState(); // Compute the state of the node current.ClipNextTickByParent(); // Perform NextTick clipping, stage 1 // Make a note to visit for stage 2, only for ClockGroups and Roots current.NeedsPostfixTraversal = (current is ClockGroup) || (current.IsRoot); } else { prefixEnumerator.SkipSubtree(); } } } // To perform a postfix walk culled by NeedsPostfixTraversal flag, we use a local recursive method // Note that since we called for this operation, it is probably already needed by the root clock ComputeTreeStateRoot(); }