MediaContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media / MediaContext.cs / 1477649 / MediaContext.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//      The MediaContext class controls the media layer. 
//
//----------------------------------------------------------------------------- 

using System;
using System.Collections;
using System.Collections.Generic; 
using System.ComponentModel;
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Threading;
using System.Windows.Threading; 
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media; 
using System.Windows.Media.Animation;
using System.Windows.Media.Composition; 
using System.Security; 
using System.Windows.Media.Effects;
 
using MS.Internal;
using MS.Internal.PresentationCore;
using MS.Utility;
using MS.Win32; 

using Microsoft.Win32.SafeHandles; 
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID; 

namespace System.Windows.Media
{
    ///  
    /// The MediaContext class controls the media layer.
    ///  
    ///  
    /// Use  to start up the media system and  to
    /// shut down the media system. 
    /// 
    /// 
    internal partial class MediaContext : DispatcherObject, IDisposable, IClock
    { 
        /// 
        /// Initializes the MediaContext's clock service. 
        ///  
        static MediaContext()
        { 
            long qpcCurrentTime;

            SafeNativeMethods.QueryPerformanceFrequency(out _perfCounterFreq);
 
            if (IsClockSupported)
            { 
                SafeNativeMethods.QueryPerformanceCounter(out qpcCurrentTime); 
            }
            else 
            {
                qpcCurrentTime = 0;
            }
 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics, EventTrace.Event.WClientQPCFrequency, _perfCounterFreq, qpcCurrentTime);
        } 
 
        /// 
        /// Returns true if the MediaContext can return current time values, 
        /// false otherwise.
        /// 
        internal static bool IsClockSupported
        { 
            get
            { 
                return _perfCounterFreq != 0; 
            }
        } 

        /// 
        /// Converts a time value expressed in "counts" (as returned by a call
        /// to QueryPerformanceCounter) to "ticks".  A Tick is the smallest 
        /// time unit expressable by a TimeSpan and is equal to 100ns
        ///  
        ///  
        /// 
        private static long CountsToTicks(long counts) 
        {
            // The following expression retains precision while avoiding overflow:
            return (long)(TimeSpan.TicksPerSecond * (counts / _perfCounterFreq) + (TimeSpan.TicksPerSecond * (counts % _perfCounterFreq)) / _perfCounterFreq);
        } 

        ///  
        /// Converts a time value expressed in "ticks" to an estimate of a count 
        /// (as returned by a call to QueryPerformanceCounter)
        ///  
        /// 
        /// 
        private static long TicksToCounts(long ticks)
        { 
            return (long)(_perfCounterFreq * (ticks / TimeSpan.TicksPerSecond) + (_perfCounterFreq * (ticks % TimeSpan.TicksPerSecond)) / TimeSpan.TicksPerSecond);
        } 
 
        /// 
        /// Finds out whether a number is prime or not 
        /// 
        /// 
        /// Fails on 2 by saying that it is not prime but we won't call the
        /// method with 2 as input 
        /// 
        private static bool IsPrime(int number) 
        { 
            // If the number is even then it's not prime.
            // This is WRONG for 2 but we don't get called with 2. 
            if ((number & 1) == 0)
                return false;

            int sqrt = (int) Math.Sqrt(number); 

            for (int i = 3; i <= sqrt; i += 2) 
            { 
                if (number % i == 0)
                { 
                    return false;
                }
            }
            return true; 
        }
 
        ///  
        /// Find the next prime number greater than number
        ///  
        private static int FindNextPrime(int number)
        {
            while (!IsPrime(++number))
            { 
                // Nothing to do
            } 
 
            return number;
        } 

        /// 
        /// The MediaContext lives in the Dispatcher and is the MediaSystem's class that keeps
        /// per Dispatcher state. 
        /// 
        internal MediaContext(Dispatcher dispatcher) 
        { 
            // We create exactly one MediaContext per thread. This is the one
            // for this thread 
            Debug.Assert(dispatcher.Reserved0 == null);

            // Initialize frame time information
            if (IsClockSupported) 
            {
                SafeNativeMethods.QueryPerformanceCounter(out _lastPresentationTime); 
                _estimatedNextPresentationTime = TimeSpan.FromTicks(CountsToTicks(_lastPresentationTime)); 
            }
 
            // Generate a unique id for our context so that we can pass this along to
            // CreateHWNDRenderTarget
            _contextGuid = Guid.NewGuid();
 
            // Create a dictionary in which we manage the CompositionTargets.
            _registeredICompositionTargets = new Dictionary(); 
 
            _renderModeMessage = new DispatcherOperationCallback(InvalidateRenderMode);
 
            // Create a notification window to listen for broadcast window messages
            _notificationWindow = new MediaContextNotificationWindow(this);

            // Connect to the MediaSystem. 
            if (MediaSystem.Startup(this))
            { 
                _isConnected = MediaSystem.ConnectChannels(this); 
            }
 
            // Subscribe to the OnDestroyContext event so that we can cleanup our state.
            _destroyHandler = new EventHandler(this.OnDestroyContext);
            Dispatcher.ShutdownFinished += _destroyHandler;
 
            _renderMessage = new DispatcherOperationCallback(RenderMessageHandler);
            _animRenderMessage = new DispatcherOperationCallback(AnimatedRenderMessageHandler); 
            _inputMarkerMessage = new DispatcherOperationCallback(InputMarkerMessageHandler); 

            // We hold off connecting ourselves to the dispatcher until we are sure that 
            // initialization will complete successfully.  In rare cases, function calls
            // earlier in this constructor throw exceptions, resulting in the MediaContext
            // being left in an uninitialized state; however, the Dispatcher could call methods
            // on the MediaContext, resulting in unpredictable behaviour (see bug 1630647). 
            //
            // NOTE: We must attach to the Dispatcher before creating a TimeManager, 
            // otherwise we will create a circular function loop where TimeManager attempts 
            // to create a Clock, which attempts to locate a MediaContext, which attempts to
            // create a TimeManager, resulting in a stack overflow. 
            dispatcher.Reserved0 = this;

            _timeManager = new TimeManager();
            _timeManager.Start(); 
            _timeManager.NeedTickSooner += new EventHandler(OnNeedTickSooner);
 
            _promoteRenderOpToInput = new DispatcherTimer(DispatcherPriority.Render); 
            _promoteRenderOpToInput.Tick += new EventHandler(PromoteRenderOpToInput);
            _promoteRenderOpToRender = new DispatcherTimer(DispatcherPriority.Render); 
            _promoteRenderOpToRender.Tick += new EventHandler(PromoteRenderOpToRender);
            _estimatedNextVSyncTimer = new DispatcherTimer(DispatcherPriority.Render);
            _estimatedNextVSyncTimer.Tick += new EventHandler(EstimatedNextVSyncTimeExpired);
 
            _commitPendingAfterRender = false;
        } 
 
        /// 
        /// Called by a message processor to notify us that our asynchronous 
        /// channel has outstanding messages that need to be pumped.
        /// 
        /// 
        /// Critical    - Calls a critical method: PeekNextMessage. 
        /// TreatAsSafe - Retrieving and processing a message from the back
        ///               channel is safe -- the channel is owned by this 
        ///               media context. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void NotifySyncChannelMessage(DUCE.Channel channel)
        {
            // empty the channel messages.
            DUCE.MilMessage.Message message; 
            while (channel.PeekNextMessage(out message))
            { 
                switch (message.Type) 
                {
                    case DUCE.MilMessage.Type.Caps: 
                    case DUCE.MilMessage.Type.SyncModeStatus:
                    case DUCE.MilMessage.Type.Presented:
                        break;
                    case DUCE.MilMessage.Type.PartitionIsZombie: 
                        // we remove the [....] channels so that if the app handles the exception
                        // it will get a new partition on the next [....] render request. 
                        _channelManager.RemoveSyncChannels(); 
                        NotifyPartitionIsZombie(message.HRESULTFailure.HRESULTFailureCode);
                        break; 

                    default:
                        HandleInvalidPacketNotification();
                        break; 
                }
            } 
        } 

        ///  
        /// Called by a message processor to notify us that our asynchronous
        /// channel has outstanding messages that need to be pumped.
        /// 
        ///  
        /// Critical    - Calls a critical method: PeekNextMessage.
        /// TreatAsSafe - Retrieving and processing a message from the back 
        ///               channel is safe -- the channel is owned by this 
        ///               media context.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal void NotifyChannelMessage()
        {
            // Since a notification message may sit in the queue while we 
            // disconnect, we need to check that we actually have a channel
            // when we receive this notification. If not, there's no harm; 
            // just skip the operation 
            if (Channel != null)
            { 
                DUCE.MilMessage.Message message;
                while (Channel.PeekNextMessage(out message))
                {
                    switch (message.Type) 
                    {
                        case DUCE.MilMessage.Type.Caps: 
                            NotifySetCaps(message.Caps.Caps); 
                            break;
 
                        case DUCE.MilMessage.Type.SyncModeStatus:
                            NotifySyncModeStatus(message.SyncModeStatus.Enabled);
                            break;
 
                        case DUCE.MilMessage.Type.Presented:
                            NotifyPresented( 
                                message.Presented.PresentationResults, 
                                message.Presented.PresentationTime,
                                message.Presented.RefreshRate 
                                );
                            break;

                        case DUCE.MilMessage.Type.PartitionIsZombie: 
                            NotifyPartitionIsZombie(message.HRESULTFailure.HRESULTFailureCode);
                            break; 
 
                        case DUCE.MilMessage.Type.BadPixelShader:
                            NotifyBadPixelShader(); 
                            break;

                        default:
                            HandleInvalidPacketNotification(); 
                            break;
                    } 
                } 
            }
        } 

        // MediaSystem is per-AppDomain and so it uses this to ensure that InvalidateRenderMode()
        // is called by the right thread.
        internal void PostInvalidateRenderMode() 
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, _renderModeMessage, null); 
        } 

        ///  
        /// Tells all of the HwndTargets to InvalidateRenderMode()
        /// 
        private object InvalidateRenderMode(object dontCare)
        { 
            Debug.Assert(CheckAccess());
 
            foreach (ICompositionTarget target in _registeredICompositionTargets.Keys) 
            {
                HwndTarget hwndTarget = target as HwndTarget; 

                if (hwndTarget != null)
                {
                    hwndTarget.InvalidateRenderMode(); 
                }
            } 
 
            return null;
        } 

        /// 
        /// NotifySetCaps - this method is called to update the graphics caps
        /// If the new render tier is different from the previous render tier, 
        /// this method will notify all TierChanged listeners.
        ///  
        ///  int - the new render tier  
        private void NotifySetCaps(MilGraphicsAccelerationCaps caps)
        { 
            PixelShaderVersion = caps.PixelShaderVersion;
            MaxPixelShader30InstructionSlots = caps.MaxPixelShader30InstructionSlots;
            HasSSE2Support = Convert.ToBoolean(caps.HasSSE2Support);
            MaxTextureSize = new Size(caps.MaxTextureWidth, caps.MaxTextureHeight); 

            int tier = caps.TierValue; 
            if (_tier != tier) 
            {
                _tier = tier; 

                if (TierChanged != null)
                {
                    TierChanged(null, null); 
                }
            } 
        } 

        ///  
        /// Internal event which is raised when a bad pixel shader is detected on this MediaContext.
        /// 
        internal event EventHandler InvalidPixelShaderEncountered;
 
        /// 
        /// NotifyBadPixelShader - this method is called when the render 
        /// thread has detected a bad pixel shader.  The render thread continues 
        /// to raise this until the problem's been corrected.  This method
        /// invokes the listeners on the static event in PixelShader. 
        /// 
        private void NotifyBadPixelShader()
        {
            if (InvalidPixelShaderEncountered != null) 
            {
                InvalidPixelShaderEncountered(null, null); 
            } 
            else
            { 
                // It's never correct to not have an event handler hooked up in
                // the case when an invalid shader is encountered.  Raise an
                // exception directing the app to hook up an event handler.
                throw new InvalidOperationException(SR.Get(SRID.MediaContext_NoBadShaderHandler)); 
            }
        } 
 
        /// 
        /// The partition this media context is connected to went into 
        /// zombie state. This means either an unhandled batch processing,
        /// rendering or presentation error and will require us to reconnect.
        /// 
        private void NotifyPartitionIsZombie(int failureCode) 
        {
            // 
            // We only get back these kinds of notification:- 
            // For all OOM cases, we get E_OUTOFMEMORY.
            // For all OOVM cases, we get D3DERR_OUTOFVIDEOMEMORY and 
            // for all other errors we get WGXERR_UCE_RENDERTHREADFAILURE.
            //

            switch (failureCode) 
            {
            case HRESULT.E_OUTOFMEMORY: 
                throw new System.OutOfMemoryException(); 
            case HRESULT.D3DERR_OUTOFVIDEOMEMORY:
                throw new System.OutOfMemoryException(SR.Get(SRID.MediaContext_OutOfVideoMemory)); 
            default:
                throw new System.InvalidOperationException(SR.Get(SRID.MediaContext_RenderThreadError));
            }
        } 

        ///  
        /// The back channel processed a malformed packet and so gives 
        /// the notification of invalid packet.
        ///  
        private void HandleInvalidPacketNotification()
        {
            //
            // NTRAID#Longhorn-2006/05/02-pravirg - 
            // For now we ignore the packet and continue processing
            // other packets. In future, we could also close this channel. 
            // 
        }
 
        /// 
        /// Tier Property - returns the current render tier for this MediaContext.
        /// 
        internal int Tier 
        {
            get 
            { 
                return _tier;
            } 
        }

        /// 
        /// PixelShaderVersion Property - returns the current PixelShader 
        /// (major<<16|minor) version
        ///  
        internal UInt32 PixelShaderVersion 
        {
            get; 
            private set;
        }

        ///  
        /// MaxPixelShader30InstructionSlots Property - returns the max number of instruction
        /// slots for PS 3.0 
        ///  
        internal UInt32 MaxPixelShader30InstructionSlots
        { 
            get;
            private set;
        }
 
        /// 
        /// HasSSE2Support Property - returns true if the processor supports SSE2 instructions 
        ///  
        internal Boolean HasSSE2Support
        { 
            get;
            private set;
        }
 
        /// 
        /// MaxTextureSize Property - returns the max texture width and height creatable by the 
        /// underlying hardware.  The API returns the minimum values across all available hardware devices. 
        /// 
        internal Size MaxTextureSize 
        {
            get;
            private set;
        } 

        ///  
        /// Internal event which is raised when the Tier changes on this MediaContext. 
        /// 
        internal event EventHandler TierChanged; 

        /// 
        /// Asks the composition engine to retrieve the current hardware tier.
        /// This tier will be sent back via NotifyChannelMessage. 
        /// 
        ///  
        /// Critical        - Contains an unsafe code block. 
        /// TreatAsSafe     - Unsafe block just uses the sizeof operator.
        ///                   Sending a message to a channel is safe. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void RequestTier(DUCE.Channel channel)
        { 
            unsafe
            { 
                DUCE.MILCMD_CHANNEL_REQUESTTIER data; 

                // 
                // Ask for the hardware tier information for the primary display
                //

                data.Type = MILCMD.MilCmdChannelRequestTier; 
                data.ReturnCommonMinimum = 0; // false
 
                channel.SendCommand( 
                    (byte*)&data,
                    sizeof(DUCE.MILCMD_CHANNEL_REQUESTTIER) 
                    );
            }
        }
 
        /// 
        /// Schedule the next rendering operation based on our presentation 
        /// mode and the next time we will have active animations. 
        /// 
        ///  
        /// Specifies the minimum time before making the next rendering operation
        /// active
        /// 
        private void ScheduleNextRenderOp(TimeSpan minimumDelay) 
        {
            // 
            // If _needToCommitChannel is true, then we are in a waiting state and we've 
            // already rendered a new frame. We don't want to render again until we've
            // hit the next VSync at which point we'll commit the rendered frame. 
            // Note that we will still render again if someone explicitely forces a
            // render (PostRender). When we do hit the next VSync and commit the channel
            // we will schedule the next render operation.
            // 
            if (!_isDisconnecting && !_needToCommitChannel)
            { 
                // 
                // This is the time at which the next animation will be active
                // in ms. (a negative value represents no animations are active) 
                //
                TimeSpan nextTickNeeded = TimeSpan.Zero;

                // 
                // If we have one or more active Rendering events it's the same
                // as having an active animation so we know that we'll need to 
                // render another frame. 
                //
                if (Rendering == null) 
                {
                    nextTickNeeded = _timeManager.GetNextTickNeeded();
                }
 
                //
                // If we have a tick in the future, make sure that it's not before 
                // the minimum delay requested. 
                //
                if (nextTickNeeded >= TimeSpan.Zero) 
                {
                    nextTickNeeded = TimeSpan.FromTicks(Math.Max(nextTickNeeded.Ticks, minimumDelay.Ticks));
                    EnterInterlockedPresentation();
                } 
                else
                { 
                    LeaveInterlockedPresentation(); 
                }
 
                // We need to tick in the distant future, schedule a far way message
                if (nextTickNeeded > TimeSpan.FromSeconds(1))
                {
                    if (_currentRenderOp == null) 
                    {
                        _currentRenderOp = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, _animRenderMessage, null); 
 
                        _promoteRenderOpToRender.Interval = nextTickNeeded;
                        _promoteRenderOpToRender.Start(); 
                    }
                }
                // We need to tick soon (< 1 second)
                else if (nextTickNeeded > TimeSpan.Zero) 
                {
                    // Only create a new render op if we don't have one 
                    // scheduled 
                    if (_currentRenderOp == null)
                    { 
                        _currentRenderOp = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, _animRenderMessage, null);

                        _promoteRenderOpToInput.Interval = nextTickNeeded;
                        _promoteRenderOpToInput.Start(); 

                        _promoteRenderOpToRender.Interval = TimeSpan.FromSeconds(1); 
                        _promoteRenderOpToRender.Start(); 
                    }
                } 
                else if (nextTickNeeded == TimeSpan.Zero)
                {
                    Debug.Assert(InterlockIsEnabled,
                        "If we are not in Interlocked Mode, we should always have a delay"); 

                    DispatcherPriority priority = DispatcherPriority.Render; 
 
                    //
                    // We normally want to schedule rendering at Render priority, however if something at 
                    // render priority takes more than a frame it will block input from ever being processed.
                    // To prevent this we create an operation at input priority which lets us know how long
                    // input has been blocked for.  If it's blocked for too long we should schedule our render
                    // at Input priority so that input can flush before we start another render. 
                    //
                    if (_inputMarkerOp == null) 
                    { 
                        _inputMarkerOp = Dispatcher.BeginInvoke(DispatcherPriority.Input, _inputMarkerMessage, null);
                        _lastInputMarkerTime = CurrentTicks; 
                    }
                    else if (CurrentTicks - _lastInputMarkerTime > MaxTicksWithoutInput)
                    {
                        priority = DispatcherPriority.Input; 
                    }
 
                    // Schedule an operation to happen immediately. 
                    if (_currentRenderOp == null)
                    { 
                        _currentRenderOp = Dispatcher.BeginInvoke(priority, _animRenderMessage, null);
                    }
                    else
                    { 
                        _currentRenderOp.Priority = priority;
                    } 
 
                    _promoteRenderOpToInput.Stop();
                    _promoteRenderOpToRender.Stop(); 
                }

                //
                // Trace the scheduling of the render 
                //
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics, EventTrace.Event.WClientScheduleRender, nextTickNeeded.TotalMilliseconds); 
            } 
        }
 
        /// 
        /// Commits the channel, but only after the next vblank has occured.
        /// 
        private void CommitChannelAfterNextVSync() 
        {
            if (_animationRenderRate != 0) 
            { 
                //
                // estimate the next vblank interval and set our timer interval to wake up at this time. 
                // we add 1ms to the estimated time because are estimated time make not be perfectly accurate
                // and its more important that wake up after the vblank than right on it.
                //
 
                long currentTicks = CurrentTicks;
                long earliestWakeupTicks = currentTicks + TicksUntilNextVsync(currentTicks) + TimeSpan.TicksPerMillisecond; 
                _estimatedNextVSyncTimer.Interval = TimeSpan.FromTicks(earliestWakeupTicks - currentTicks); 
                _estimatedNextVSyncTimer.Tag = earliestWakeupTicks;
            } 
            else
            {
                // It's possible our first notification from the UCE didn't give us the
                // refresh rate.  We can't estimate when vsync will be - try again in about a vblank 
                _estimatedNextVSyncTimer.Interval = TimeSpan.FromMilliseconds(17);
            } 
 
            _estimatedNextVSyncTimer.Start();
 
            //
            // We are waiting for the next VBlank to occur
            //
            _interlockState = InterlockState.WaitingForNextFrame; 
            _lastPresentationResults = MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_NOPRESENT;
        } 
 
        /// 
        /// Processes the Presented composition engine notification. 
        /// 
        /// 
        /// The results of the last presentation.
        ///  
        /// 
        /// The timestamp of the last presentation. 
        ///  
        /// 
        /// The current display refresh rate. 
        /// 
        private void NotifyPresented(
            MIL_PRESENTATION_RESULTS presentationResults,
            long presentationTime, 
            int displayRefreshRate
            ) 
        { 
            if (InterlockIsEnabled)
            { 
                Debug.Assert(_interlockState == InterlockState.WaitingForResponse,
                    "We should not be getting a notification unless we asked for one");

                // 
                // The composition engine has presented, so we are ready to start
                // another frame, if necessary. Also remember the presentation 
                // time to use as the basis for estimating the time for the next 
                // frame.
                // 

                //
                // presentationDelay represents the time we want to wait until
                // we activate a new render operation. 
                //
                TimeSpan presentationDelay = TimeSpan.Zero; 
                _lastPresentationResults = presentationResults; 

                // 
                // The UCE has processed our frame. So we are not waiting on it
                // anymore. Set our state to idle.
                //
                _interlockState = InterlockState.Idle; 

                switch (presentationResults) 
                { 
                    case MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_VSYNC:
                        { 
                            // Adjust the refresh rate to prevent constant tearing
                            // on the screen. We've chosen NextPrime(RefreshRate+5)
                            // as a function that seems to look good at all
                            // popular refresh rates. 
                            // Only update the adjusted refresh rate when the
                            // display refresh rate changes. If changing this 
                            // make sure to look at the performance of the lookup 
                            // of the adjusted refresh rate.
                            if (displayRefreshRate != _displayRefreshRate) 
                            {
                                _displayRefreshRate = displayRefreshRate;
                                _adjustedRefreshRate = FindNextPrime(displayRefreshRate + 5);
                            } 

                            // VSync means that the UCE has presented at the time we 
                            // requested, but it can still tear, so if the user has 
                            // requested a high framerate through DFR, override the
                            // monitor refresh rate with this DFR. 
                            _animationRenderRate = Math.Max(_adjustedRefreshRate, _timeManager.GetMaxDesiredFrameRate());
                            _lastPresentationTime = presentationTime;
                        }
                        break; 

                    case MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_VSYNC_UNSUPPORTED: 
                        { 
                            //
                            // If we don't support VSync then wait a small delay so that we don't 
                            // just overrun the UCE
                            //
                            presentationDelay = _timeDelay;
                        } 
                        break;
 
                    case MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_NOPRESENT: 
                        {
                            // 
                            // We didn't present because the scene didn't change.
                            // Since the UCE returned early, the vblank at which
                            // we were hoping to present the last frame has not
                            // occurred yet.  We will set a timer to trigger at 
                            // the time at which we think that vblank will occur
                            // (with a fudge factor of 1 ms to ensure we don't 
                            // wake up before) 
                            //
                            // Until the timer expires we will not send any updates 
                            // to the UCE because we don't know if we'll be able to
                            // hit the vblank. We will update the Visual tree and
                            // queue the UCE changes, but we will not commit yet.
                            // 

                            Debug.Assert(!InterlockIsWaiting, 
                                "We should not be waiting at this point"); 

                            CommitChannelAfterNextVSync(); 
                        }
                        break;

                        // This return code represents that we've presented with 
                        // the DWM, so there is no tearing, we don't need to
                        // override the refresh rate in this case. 
                    case MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_DWM: 
                        {
                            // In the DWM case these values are actually correct, so we update them here 
                            _animationRenderRate = displayRefreshRate;
                            _lastPresentationTime = presentationTime;
                        }
                        break; 
                }
 
                // Cap our Animation RenderRate to 1000 fps. 
                _animationRenderRate = Math.Min(_animationRenderRate, 1000);
 
                //
                // Trace the notification from the UCE
                //
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Event.WClientUceNotifyPresent, _lastPresentationTime, (Int64)presentationResults); 

                if (presentationResults == MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_NOPRESENT) 
                { 
                    // dont commit or schedule because we've created a timer to do this.
                    // the only reason we're waiting until here to do this is because we want the ETW event to fire 
                    return;
                }
                else
                { 
                    // if we get any result other than a NOPRESENT then we should stop the _estimatedNextVSyncTimer timer
                    // so we dont end up commiting twice in 1 vblank interval 
                    _estimatedNextVSyncTimer.Stop(); 
                }
 
                //
                // We want to schedule the next render operation before commiting the
                // channel so that we can send the command right away.
 
                //
                // If we already had a render occur then we've ticked the 
                // time manager and layout has been updated send this frame 
                // to the UCE.
                // 

                if (!InterlockIsWaiting && _needToCommitChannel)
                {
                    // 
                    // if we've already commit during this vblank interval, dont do it again
                    // until the following vblank 
                    // 

                    if (HasCommittedThisVBlankInterval) 
                    {
                        CommitChannelAfterNextVSync();
                        return;
                    } 

                    CommitChannel(); 
 
                    Debug.Assert(InterlockIsWaiting,
                        "We had something to commit, we should be waiting for that"+ 
                        "notification to come back");
               }

                ScheduleNextRenderOp(presentationDelay); 
            }
        } 
 

        ///  
        /// Return true if the current time is within the same vblank interval as our last commit time
        /// 
        private bool HasCommittedThisVBlankInterval
        { 
            get
            { 
                if (_animationRenderRate == 0) 
                    return false;
 
                // is our last commit within 1 refresh period of our current time?
                //
                if (CurrentTicks - _lastCommitTime < RefreshPeriod)
                { 
                    // if the last commit is later than the last presentation, then we
                    // have committed a frame and haven't been notified of it yet. 
                    // in this case, we dont want to commit the channel again 
                    if (_lastCommitTime > CountsToTicks(_lastPresentationTime))
                        return true; 
                }

                return false;
            } 
        }
 
 
        /// 
        /// Returns the current time in Ticks (100 ns intervals). 
        /// 
        internal static long CurrentTicks
        {
            get 
            {
                long counts; 
                SafeNativeMethods.QueryPerformanceCounter(out counts); 
                return CountsToTicks(counts);
            } 
        }

        //
        //private long CurrentTimeInMs 
        //{
        //    get 
        //    { 
        //        return CurrentTicks / TimeSpan.TicksPerMillisecond;
        //    } 
        //}
        //

        ///  
        /// Returns the time in Ticks (100 ns intervals) between vsyncs.
        /// It's up to the caller to ensure that _animationRenderRate is valid. 
        ///  
        private long RefreshPeriod
        { 
            get
            {
                return TimeSpan.TicksPerSecond / _animationRenderRate;
            } 
        }
 
 
        /// 
        /// Computes the number of ticks since the last UCE present 
        /// 
        /// 
        /// 
        private long TicksSinceLastPresent(long currentTime) 
        {
            return currentTime - CountsToTicks(_lastPresentationTime); 
        } 

 
        /// 
        /// Estimates the time in Ticks (100 ns intervals) since the
        /// last vsync.  It starts with the last presentation time
        /// and extrapolates based on the display refresh rate. 
        /// 
        private long TicksSinceLastVsync(long currentTime) 
        { 
            return TicksSinceLastPresent(currentTime) % RefreshPeriod;
        } 


        /// 
        /// Estimates the number of ticks until the next vsync 
        /// 
        ///  
        ///  
        private long TicksUntilNextVsync(long currentTime)
        { 
            return RefreshPeriod - TicksSinceLastVsync(currentTime);
        }

        ///  
        /// Processes the SyncMode composition engine notification.
        ///  
        ///  
        /// The HRESULT of enabling [....] mode.
        ///  
        private void NotifySyncModeStatus(int enabledResult)
        {
            //
            // Only process the notification if we asked to start interlocked 
            // presentation mode
            // 
            if (_interlockState == InterlockState.RequestedStart) 
            {
                if (enabledResult >= 0) 
                {
                    //
                    // We succeded in entering the interlocked mode in the
                    // composition engine. 
                    //
 
                    _interlockState = InterlockState.Idle; 

                    if (Channel != null) 
                    {
                        // SyncFlush will Commit()
                        if (CommittingBatch != null)
                        { 
                            CommittingBatch(Channel, new EventArgs());
                        } 
 
                        Channel.SyncFlush();
                    } 
                }
                else
                {
                    _interlockState = InterlockState.Disabled; 
                }
            } 
        } 

        ///  
        /// Estimates the timestamp of the next frame to be presented
        /// 
        /// 
        /// 
        /// How the current time is computed
        /// ================================ 
        /// 
        /// The MediaContext keeps track of when frames are presented to the
        /// display. The "current time" for the MediaContext is actually the 
        /// time at which we estimate the next frame to be presented. This
        /// allows the system to produce a frame that is correct at the time
        /// it is seen by the user, leading to smooth animations. The main
        /// assumption at this time is that a frame can be compiled, composed 
        /// and presented before the next vertical refresh, whenever that may
        /// be. A future refinement of this algorithm will take historical 
        /// profiling data into account to compute a more accurate target 
        /// presentation frame.
        /// 
        ///
        /// The next frame time is estimated by taking the current system
        /// time and rounding that up to the next refresh time, based on the
        /// last refresh time and the current display refresh rate. 
        ///
        /// This is the situation when we are ready to submit a new frame: 
        /// 
        ///   refresh:   -|  F  |-
        /// --+----[+]----+-----+-----+--*-(+)----+-----+  t 
        ///         L                    C  N
        ///         last frame        now   next frame
        ///
        /// 
        /// In order to pipeline the UI thread and the composition thread we
        /// will try to start rendering the next frame while the composition 
        /// is presenting the last frame that we've sent it. This allows us to 
        /// make use of 2 CPUs more efficiently (UI thread can renders 1 frame
        /// while the composition thread is presenting another). So when 
        /// calculating the current time, if we are waiting on a frame already
        /// then we will produce the frame for the VSync after. That means if we
        /// are in the wait state then the next presentation time is not the
        /// next refresh time, but at least *two* refresh times in the future, 
        /// because the next refresh is when the *last* frame will be presented.
        /// 
        /// This is the situation when we are in a wait state: 
        ///
        ///   refresh:   -|  F  |- 
        /// --+----[+]----+-----+-----+--*--+----(+)----+  t
        ///         L                    C        N
        ///         last frame        now         next frame
        /// 
        ///
        /// The computation of the next frame time "N", then, involves four 
        /// variables: 
        ///
        ///     Variable                            Units 
        ///     -------------------------------------------------------------
        ///     L   The last presentation time      Ticks
        ///     C   The current actual time         Ticks
        ///     R   The display refresh rate        frames per second 
        ///     W   The wait state                  boolean -- true if waiting
        /// 
        /// The computation is fairly straightforward.  We take the time since 
        /// we've last presented (C - L) and mod it with the refresh rate (R) to get
        /// the time since the last vsync.  We can then get the time until the next 
        /// vsync by subtracting the refresh rate (R) from that value.
        /// TicksSinceLastVsync() and TicksUntilNextVsync() implement this.
        ///
        /// If we're not waiting we can render at the next vsync.  If we are waiting, 
        /// we'll wait until the vsync after. This computation can get slightly
        /// hairy if we're exactly on a frame boundary; the comments inside 
        /// IClock.CurrentTime explain this a bit more. 
        ///
        /// Each time the composition engine notifies us that a frame 
        /// was presented it also tells us the timestamp for that frame. At
        /// that time we also leave the wait state. If we successfully present
        /// on every refresh then two consecutive computations should give the
        /// same result.  However,  the values of L we get from the composition 
        /// engine are subject to fluctuations (due to thread scheduling issues),
        /// so we may in fact compute different time values, either earlier or later. 
        /// To avoid thrashing the Tick and Layout processes we keep the previous 
        /// estimate if the new estimate is within half a frame of it. If the
        /// new estimate is later than that then it means the previous estimate 
        /// was too inaccurate, potentially because we actually took longer
        /// than a single refresh to compile, compose and present the last
        /// frame. In that case we abandon the previous estimated value.
        /// 
        /// 
        TimeSpan IClock.CurrentTime 
        { 
            get
            { 
                Debug.Assert(IsClockSupported, "MediaContext.CurrentTime called when QueryPerformaceCounter is not supported");

                long counts;
                SafeNativeMethods.QueryPerformanceCounter(out counts); 

                long countsTicks = CountsToTicks(counts); 
 
                if (_interlockState != InterlockState.Disabled)
                { 
                    //
                    // On the first frame we haven't yet received information about
                    // the display refresh rate from the compositor. In that case
                    // we can't snap to frame times, so we simply use the current 
                    // time.
                    // 
 
                    if (   _animationRenderRate != 0
                        && _lastPresentationResults != MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_VSYNC_UNSUPPORTED) 
                    {
                        //
                        // Figure out where we are in the vsync period. The entire computation
                        // is done in TimeSpan Ticks (100ns intervals) 
                        //
 
                        long nextVsyncTicks;        // Absolute time in ticks at which the next vsync will occur 
                        long vsyncAdvance;          // We expect to present this many vsyncs in the future
                        long nextPresentationTicks; // Absolute time in ticks at which we expect to present 


                        // Future: eventually we should actually keep track of how long it takes
                        // the UCE to present.  For now we assume it's one vsync 
                        _averagePresentationInterval = RefreshPeriod;
 
 
                        //
                        // Compute how many frames in the future we expect to present to. 
                        //

                        //
                        // We have to be very careful about the computation when we're on a frame boundary. 
                        //
                        // If the time since last vsync is very small it means that we, by computation, think a vsync 
                        // just happened.  In reality, it may have just happened, is happening, or will happen very soon 
                        // Since the UCE is on a different thread, it's possible in all three cases that the UCE is about
                        // to notify us of the vsync. 
                        //
                        // This can get us into a situation where, though the computed time since last vsync is small,
                        // the timeSinceLastPresent is one frame ago. Either the UCE is currently presenting and
                        // timeSinceLastPresent is stale (i.e. we just haven't been notified yet), or the UCE will be 
                        // missing this particular vsync and will return from present on a subsequent vsync.
                        // 
                        // This is a problem when we're in a wait state (we've previously committed a frame and are 
                        // waiting for the UCE to return from presenting it). If the UCE is about to return, we
                        // can tick the current frame to the next vsync.  If the UCE is not about to return, we 
                        // must tick the current frame in the future.
                        //
                        // The best way to disambiguate this is to look up the last time a frame was committed.
                        // This is how long the UCE has been working on it.  By comparing that with how long the UCE 
                        // takes on average to present a frame we'll be able to determine if it is finishing it now.
 
                        vsyncAdvance = 0; 

                        if (InterlockIsWaiting) 
                        {
                            // If we're waiting for the UCE to finish presenting a frame then
                            // in most cases it'll come back at the next vsync and we'll set our
                            // render time to the one after. 
                            // We once (v1 shipping code) attempted to limit vsyncAdvance to 0
                            // based on heuristics involving where in the frame we are.  This was 
                            // removed after it was discovered that the logic was flawed and it made 
                            // analyzing timing graphs more difficult.
                            vsyncAdvance = 1; 
                        }

                        nextVsyncTicks = countsTicks + TicksUntilNextVsync(countsTicks);
                        nextPresentationTicks = (nextVsyncTicks + (vsyncAdvance * RefreshPeriod)); 

                        // 
                        // If we had previously estimated the next presentation time 
                        // and that estimate still seems reasonable then use the
                        // previous estimate rather than the newly computed value. 
                        // This is a good performance win because it means we will
                        // tick animations and thus run layout to the same value as
                        // last time, which saves a lot of computation. For this
                        // purpose, we will consider the previous estimate "reasonable" 
                        // if it falls within 1/2 frame of the new value.
                        // 
 
                        if ((nextPresentationTicks - _estimatedNextPresentationTime.Ticks) * _animationRenderRate > TimeSpan.FromMilliseconds(500).Ticks)
                        { 
                            // Establish a new estimate
                            _estimatedNextPresentationTime = TimeSpan.FromTicks(nextPresentationTicks);
                        }
                    } 
                    else
                    { 
                        _estimatedNextPresentationTime = TimeSpan.FromTicks(countsTicks); 
                    }
                } 
                else
                {
                    _estimatedNextPresentationTime = TimeSpan.FromTicks(countsTicks);
                } 

                return _estimatedNextPresentationTime; 
            } 
        }
 
        /// 
        /// Starts up the media system and creates needed channels used by the media context
        /// 
        internal void CreateChannels() 
        {
            _channelManager.CreateChannels(); 
 
            HookNotifications();
 
            // Create an ETW Event Resource for performance tracing
            // [....]: It might be good enough to put this in the current batch without
            // submitting it.
            _uceEtwEvent.CreateOrAddRefOnChannel(this, Channel, DUCE.ResourceType.TYPE_ETWEVENTRESOURCE); 

            // Send a request for an updated render tier value 
            RequestTier(Channel); 

            Channel.CloseBatch(); 
            Channel.Commit();

            // We now call CompleteRender, which calls SyncFlush, to ensure that all commands have
            // been processed, including the tier request. 
            CompleteRender();
 
            // Since all of the commands have now been processed, we can go ahead and manually call 
            // NotifyChannelMessage to pick up any back channel messages which have been sent.
            NotifyChannelMessage(); 
        }

        /// 
        /// Starts releases channels and shuts down the media system. When the last visual manager has 
        /// disconnected the transport will shut down.
        ///  
        private void RemoveChannels() 
        {
            // This test is needed because this method is called by Dispose which is called 
            // on shutdown which can happen in a disconnected state. We can replace this
            // test with an assert by moving
            // the management of transport connectedness state to the media system.
            if (Channel != null) 
            {
                _uceEtwEvent.ReleaseOnChannel(Channel); 
 
                //
                // With no channels left open, we cannot be in an interlocked 
                // presentation mode because we don't have a connection to the
                // composition engine.
                //
 
                LeaveInterlockedPresentation();
            } 
 
            _channelManager.RemoveChannels();
        } 

        /// 
        /// Start interlocked presentation mode and resquest
        ///  
        /// 
        /// Critical        - Contains an unsafe code block. 
        /// TreatAsSafe     - Unsafe block just uses the sizeof operator. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void EnterInterlockedPresentation()
        {
            if (!InterlockIsEnabled)
            { 
                if (MediaSystem.AnimationSmoothing
                    && Channel.MarshalType == ChannelMarshalType.ChannelMarshalTypeCrossThread 
                    && IsClockSupported) 
                {
                    unsafe 
                    {
                        //
                        // Ask the UCE to get into VSync mode
                        // 

                        DUCE.MILCMD_PARTITION_SETVBLANKSYNCMODE data; 
                        data.Type = MILCMD.MilCmdPartitionSetVBlankSyncMode; 
                        data.Enable = 1; /* true */
 
                        Channel.SendCommand(
                            (byte*)&data,
                            sizeof(DUCE.MILCMD_PARTITION_SETVBLANKSYNCMODE),
                            true); 

                        _interlockState = InterlockState.RequestedStart; 
                    } 
                }
            } 
        }


        ///  
        /// Leaves interlocked presentation mode and cleans up state so we can
        /// continue to present in non-interlocked mode. 
        ///  
        /// 
        /// Critical        - Contains an unsafe code block. 
        /// TreatAsSafe     - Unsafe block just uses the sizeof operator.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void LeaveInterlockedPresentation() 
        {
            bool interlockDisabled = (_interlockState == InterlockState.Disabled); 
 
            if (_interlockState == InterlockState.WaitingForResponse)
            { 
                // Process messages until we get a response for the outstanding frame.
                // This is necessary because we are unsure whether the UCE has already
                // posted a notification in our message queue
                CompleteRender(); 
            }
 
            // If we are waiting for the next frame stop the timer since we are 
            // leaving interlocked presentation mode
 
            _estimatedNextVSyncTimer.Stop();

            //
            // If we are not disabled then request to stop the mode. 
            // If we had already asked to start but haven't gotten the response
            // still request to stop 
            // 
            if (!interlockDisabled)
            { 
                _interlockState = InterlockState.Disabled;

                unsafe
                { 
                    //
                    // Tell the UCE that we are not in VSync mode anymore 
                    // 

                    DUCE.MILCMD_PARTITION_SETVBLANKSYNCMODE data; 
                    data.Type = MILCMD.MilCmdPartitionSetVBlankSyncMode;
                    data.Enable = 0; /* false */

                    Channel.SendCommand( 
                        (byte*)&data,
                        sizeof(DUCE.MILCMD_PARTITION_SETVBLANKSYNCMODE), 
                        true); 

                    // We need to send this notification now otherwise, we don't 
                    // know when we'll send the next packet. This will clear the
                    // channel and render the last frame (if necessary).
                    _needToCommitChannel = true;
                    CommitChannel(); 
                }
 
            } 

            Debug.Assert(_interlockState == InterlockState.Disabled, 
                "LeaveInterlockedPresentationMode should set the InterlockedState to Disabled");
        }

        ///  
        /// Hooks the async channel so we get notifications.
        ///  
        private void HookNotifications() 
        {
            Debug.Assert(Channel != null); 

            //
            // This associates this channel with the given notification
            // window so that we can receive a window message whenever 
            // there is a new message posted.
            // 
            _notificationWindow.SetAsChannelNotificationWindow(); 

            // 
            // This actually populates the channel into the composition
            // engine so that it can receive notifications.
            //
            RegisterForNotifications(Channel); 
        }
 
        ///  
        /// Gets the MediaContext from the context passed in as argument.
        ///  
        internal static MediaContext From(Dispatcher dispatcher)
        {
            Debug.Assert(dispatcher != null, "Dispatcher required");
            MediaContext cm = (MediaContext)dispatcher.Reserved0; 
            if (cm == null)
            { 
                cm = new MediaContext(dispatcher); 
                Debug.Assert(dispatcher.Reserved0 == cm);
            } 

            return cm;
        }
 
        /// 
        /// Gets the MediaContext from the current UI context. 
        ///  
        internal static MediaContext CurrentMediaContext
        { 
            get
            {
                return From(Dispatcher.CurrentDispatcher);
            } 
        }
 
 
        /// 
        /// Called by the Dispatcher to let us know that we are going away. 
        /// 
        void OnDestroyContext(object sender, EventArgs e)
        {
            Debug.Assert(CheckAccess()); 
            Dispose();
        } 
 
        /// 
        /// Disposes the MediaContext. 
        /// 
        /// 
        /// Critical - Shuts down the queue item promoter, effectively disabling rendering
        ///            when animation smoothing is turned on. 
        /// TreatAsSafe - At the time the media context object gets disposed, no rendering
        ///               is supposed to be happening anymore, so it is safe to shut down 
        ///               the queue item promoter. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public virtual void Dispose()
        {
            Debug.Assert(CheckAccess());
 
            if (!_isDisposed)
            { 
                // 

 

                // Dispose all still registered ICompositionTargets ----------------
                // Note that disposing the CompositionTargets should be the first thing we do here.
 
                // First make a copy of the dictionarys contents, because ICompositionTarget.Dispose modifies this collection.
                ICompositionTarget[] registeredVTs = new ICompositionTarget[_registeredICompositionTargets.Count]; 
                _registeredICompositionTargets.Keys.CopyTo(registeredVTs, 0); 

                // Iterate through the ICompositionTargets and dispose them. Be careful, ICompositionTarget.Dispose 
                // removes the ICompositionTargets from the Dictionary. This is why we don't iterate the Dictionary directly.
                foreach (ICompositionTarget iv in registeredVTs)
                {
                    iv.Dispose(); 
                }
                _registeredICompositionTargets = null; 
 
                // Dispose the notification window
                _notificationWindow.Dispose(); 

                // Unhook the context destroy event handler -------------------
                Dispatcher.ShutdownFinished -= _destroyHandler;
                _destroyHandler = null; 

                // Dispose the time manager ---------------------------------- 
                Debug.Assert(_timeManager != null); 
                _timeManager.NeedTickSooner -= new EventHandler(OnNeedTickSooner);
                _timeManager.Stop(); 

                // From now on we are disposed -------------------------------
                _isDisposed = true;
 
                RemoveChannels();
 
                // if we set the Dispatcher.Reserved0 field to null, we end 
                // creating another media context on the shutdown pass when the
                // HwndSrc class sets its visual root to null. In a disconnected 
                // state this attempts to re open the transport.

                // Disconnect from MediaSystem -------------------------------
                MediaSystem.Shutdown(this); 
                _timeManager = null;
 
                GC.SuppressFinalize(this); 
            }
        } 

        /// 
        /// Registers a new ICompositionTarget with the MediaSystem.
        ///  
        /// Dispatcher with which the ICompositionTarget should be registered.
        /// The ICompositionTarget to register with the MediaSystem. 
        internal static void RegisterICompositionTarget(Dispatcher dispatcher, ICompositionTarget iv) 
        {
            Debug.Assert(dispatcher != null); 
            Debug.Assert(iv != null);

            MediaContext current = From(dispatcher);
            current.RegisterICompositionTargetInternal(iv); 
        }
 
        ///  
        /// Registers the ICompositionTarget from this MediaContext.
        ///  
        /// 
        private void RegisterICompositionTargetInternal(ICompositionTarget iv)
        {
            Debug.Assert(!_isDisposed); 
            Debug.Assert(iv != null);
 
            // If channel is not available, we are in a disconnected state. 
            // When connect handler is invoked for this media context, all
            // registered targets will be visited and AddRefChannel will be 
            // called for them, so here we just skip the operation.
            if (Channel != null)
            {
                // if _currentRenderingChannel is nonempty, we're registering this ICompositionTarget 
                // from within a render walk and it is thus a visualbrush, we need to add it to the
                // channel which we are currently rendering. If _currentRenderingChannel 
                // is null, we just get the target channels for this ICompositionTarget and add 
                // there.
                DUCE.ChannelSet channelSet = (_currentRenderingChannel == null) ? GetChannels() : _currentRenderingChannel.Value; 
                iv.AddRefOnChannel(channelSet.Channel, channelSet.OutOfBandChannel);
            }

            _registeredICompositionTargets.Add(iv, null); // We use the dictionary just as a set. 
        }
 
        ///  
        /// Unregisters the ICompositionTarget from the Dispatcher.
        ///  
        /// 
        /// 
        internal static void UnregisterICompositionTarget(Dispatcher dispatcher, ICompositionTarget iv)
        { 
            Debug.Assert(dispatcher != null);
            Debug.Assert(iv != null); 
 
            MediaContext.From(dispatcher).UnregisterICompositionTargetInternal(iv);
        } 

        /// 
        /// Removes the ICompositionTarget from this MediaContext.
        ///  
        /// ICompositionTarget to unregister.
        ///  
        /// Critical    - Calls a security critical method: SetNotificationWindow. 
        /// TreatAsSafe - It is ok for the MediaContext to call SetNotificationWindow on
        ///               the channel because the MediaContext owns the channel. In addition 
        ///               no user data is passed along.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void UnregisterICompositionTargetInternal(ICompositionTarget iv) 
        {
            Debug.Assert(iv != null); 
 
            // this test is needed because we always unregister the target when the ReleaseUCEResources
            // is called on the target and Dispose is called from both the media context and the 
            // hwnd source, so when shutting down in a disconnected state we end up calling here
            // after a Dispose.
            if (_isDisposed)
            { 
                return;
            } 
 
            // If channel is not available, we are in a disconnected state, which means
            // that all resources have been released and we can just skip the operation. 
            if (Channel != null)
            {
                // if _currentRenderingChannel is nonempty, we're unregistering this ICompositionTarget
                // from within a render walk and it is thus a visualbrush, we need to remove it from the 
                // channel which we are currently rendering. If _currentRenderingChannel
                // is null, we just get the target channels for this ICompositionTarget and release 
                // there. 
                DUCE.ChannelSet channelSet = (_currentRenderingChannel == null) ? GetChannels() : _currentRenderingChannel.Value;
                iv.ReleaseOnChannel(channelSet.Channel, channelSet.OutOfBandChannel); 
            }

            _registeredICompositionTargets.Remove(iv);
        } 

        private class InvokeOnRenderCallback 
        { 
            private DispatcherOperationCallback _callback;
            private object _arg; 

            public InvokeOnRenderCallback(
                DispatcherOperationCallback callback,
                object arg) 
            {
                _callback = callback; 
                _arg = arg; 
            }
 
            public void DoWork()
            {
                _callback(_arg);
            } 
        }
 
        internal void BeginInvokeOnRender( 
            DispatcherOperationCallback callback,
            object arg) 
        {
            Debug.Assert(callback != null);

            // While technically it could be OK for the arg to be null, for now 
            // I know that arg represents the this reference for the layout
            // process and should never be null. 
            Debug.Assert(arg != null); 

            if (_invokeOnRenderCallbacks == null) 
            {
                _invokeOnRenderCallbacks = new FrugalObjectList();
            }
 
            _invokeOnRenderCallbacks.Add(new InvokeOnRenderCallback(callback, arg));
 
            if (!_isRendering) 
            {
                PostRender(); 
            }
        }

        ///  
        /// Add a pending loaded or unloaded callback
        ///  
        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal LoadedOrUnloadedOperation AddLoadedOrUnloadedCallback(
            DispatcherOperationCallback callback, 
            DependencyObject target)
        {
            LoadedOrUnloadedOperation op = new LoadedOrUnloadedOperation(callback, target);
 
            if (_loadedOrUnloadedPendingOperations == null)
            { 
                _loadedOrUnloadedPendingOperations = new FrugalObjectList(1); 
            }
 
            _loadedOrUnloadedPendingOperations.Add(op);

            return op;
        } 

        ///  
        /// Remove a pending loaded or unloaded callback 
        /// 
        [FriendAccessAllowed] // Built into Core, also used by Framework. 
        internal void RemoveLoadedOrUnloadedCallback(LoadedOrUnloadedOperation op)
        {
            Debug.Assert(op != null);
 
            // cancel the operation - this prevents it from running even if it has
            // already been copied into the local array in FireLoadedPendingCallbacks 
            op.Cancel(); 

            if (_loadedOrUnloadedPendingOperations != null) 
            {
                for (int i=0; i<_loadedOrUnloadedPendingOperations.Count; i++)
                {
                    LoadedOrUnloadedOperation operation = _loadedOrUnloadedPendingOperations[i]; 
                    if (operation == op)
                    { 
                        _loadedOrUnloadedPendingOperations.RemoveAt(i); 
                        break;
                    } 
                }
            }
        }
 
        /// 
        /// If there is already a render operation in the Dispatcher queue, this 
        /// method will bump it up to render priority.  If not, it will add a 
        /// render operation at render priority.
        ///  
        /// 
        /// This method should only be called when a render is necessary "right
        /// now."  Events such as a change to the visual tree would result in
        /// this method being called. 
        /// 
        internal void PostRender() 
        { 
            // this is now needed because we no longer set Dispatcher.Reserved0 to null
            // in the Dispose method. See comment in the Dispose method. 
            if (_isDisposed)
            {
                return;
            } 
            Debug.Assert(CheckAccess());
 
 
            if (!_isRendering)
            { 
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Event.WClientPostRender);

                if (_currentRenderOp != null)
                { 
                    // If we already have a render operation in the queue, we should
                    // change its priority to render priority so it happens sooner. 
                    _currentRenderOp.Priority = DispatcherPriority.Render; 
                }
                else 
                {
                    // If we don't have a render operation in the queue, add one at
                    // render priority.
                    _currentRenderOp = Dispatcher.BeginInvoke(DispatcherPriority.Render, _renderMessage, null); 
                }
 
                // We don't need to keep our promotion timers around. 
                _promoteRenderOpToInput.Stop();
                _promoteRenderOpToRender.Stop(); 
            }
        }

        ///  
        /// This method is invoked from the HwndTarget when the window is resize.
        /// It will cancel pending render queue items and then run the dispatch for the 
        /// render queue item by hand. 
        /// 
        internal void Resize(ICompositionTarget resizedCompositionTarget) 
        {
            // Cancel pending render queue items so that we don't dispatch them later
            // causing a double render during Resize. (Note that RenderMessage will schedule a
            // new RenderQueueItem). 
            if (_currentRenderOp != null)
            { 
                _currentRenderOp.Abort(); 
                _currentRenderOp = null;
            } 

            // We don't need to keep our promotion timers around.
            _promoteRenderOpToInput.Stop();
            _promoteRenderOpToRender.Stop(); 

            // Now render manually directly from the resize handler. 
            // Alternatively we could pump the message queue here with a filter that only allows 
            // RenderQueueItems to get dispatched.
            RenderMessageHandler(resizedCompositionTarget); 
        }

        /// 
        /// This is the standard RenderMessageHandler callback, posted via PostRender() 
        /// and Resize().  This wraps RenderMessageHandlerCore and emits an ETW events
        /// to trace its execution. 
        ///  
        private object RenderMessageHandler(
              object resizedCompositionTarget /* can be null if we are not resizing*/ 
            )
        {
            if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
            { 
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientRenderHandlerBegin, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, PerfService.GetPerfElementID(this));
            } 
 
            RenderMessageHandlerCore(resizedCompositionTarget);
 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Event.WClientRenderHandlerEnd);

            return null;
        } 

 
        ///  
        /// This is the RenderMessageHandler callback posted by RenderMessageHandlerCore
        /// when animations are active 
        /// This wraps RenderMessageHandlerCore and emits an ETW event to signify that
        /// the Render Message being handled is processing an animation.
        /// 
 
        private object AnimatedRenderMessageHandler(
            object resizedCompositionTarget /* can be null if we are not resizing*/ 
            ) 
        {
            if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info)) 
            {
                EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientAnimRenderHandlerBegin, EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, PerfService.GetPerfElementID(this));
            }
 
            RenderMessageHandlerCore(resizedCompositionTarget);
 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Event.WClientAnimRenderHandlerEnd); 

            return null; 

        }

        ///  
        /// This handles the _inputMarkerOp message.  We're using
        /// _inputMarkerOp to determine if input priority dispatcher ops 
        /// have been processes. 
        /// 
        private object InputMarkerMessageHandler(object arg) 
        {
            //set the marker to null so we know that input priority has been processed
            _inputMarkerOp = null;
            return null; 
        }
 
        //static int queueItemID; 

        ///  
        /// The ol' RenderQueueItem.
        /// 
        /// 
        ///     Critical: Since it calls to InputManager.UnsecureCurrent 
        ///     TreatAsSafe: Since it does not expose the InputManager
        ///  
        [SecurityCritical,SecurityTreatAsSafe] 
        private void RenderMessageHandlerCore(
            object resizedCompositionTarget /* can be null if we are not resizing*/ 
            )
        {
            // if the media system is disconnected bail.
            if (Channel == null) 
            {
                return; 
            } 

            Debug.Assert(CheckAccess()); 
            Debug.Assert(
                (resizedCompositionTarget == null) ||
                (resizedCompositionTarget is ICompositionTarget));
 
            _isRendering = true;
 
            // We don't need our promotion timers anymore. 
            _promoteRenderOpToInput.Stop();
            _promoteRenderOpToRender.Stop(); 

            bool gotException = true;

            try 
            {
                int tickLoopCount = 0; 
 

                do 
                {
                    tickLoopCount++;
                    if (tickLoopCount > 153)
                    { 
                        throw new InvalidOperationException(SR.Get(SRID.MediaContext_InfiniteTickLoop));
                    } 
 
                    _timeManager.Tick();
 
                    // Although the timing tree is now clean, during layout
                    // more animations may be added, in which case we must tick
                    // again to prevent "first frame" problems. If we do tick
                    // again we want to do so at the same time as before, until 
                    // we are done. To that end, lock the tick time to the
                    // first tick. Note that Lock/Unlock aren't counted, so we 
                    // can call Lock inside the loop and still safely call 
                    // Unlock just once after we are done.
                    _timeManager.LockTickTime(); 

                    // call all render callbacks
                    FireInvokeOnRenderCallbacks();
 
                    // signal that the frame has been updated and we are ready to render.
                    // only fire on the first iteration 
                    if (Rendering != null && tickLoopCount==1) 
                    {
                        // The RenderingEventArgs class stores the next estimated presentation time. 
                        // Since the TimeManager has just ticked, LastTickTime is exactly this time.
                        // (TimeManager gets its tick time from MediaContext's IClock implementation).
                        // In the case where we can't query QPC or aren't doing interlocked presents,
                        // this will be equal to the current time, which is a good enough approximation. 
                        Rendering(this.Dispatcher, new RenderingEventArgs(_timeManager.LastTickTime));
 
                        // call all render callbacks again in case the Rendering event affects layout 
                        // this will enable layout effecting changes to get triggered this frame
                        FireInvokeOnRenderCallbacks(); 
                    }
                }
                while (_timeManager.IsDirty);
 
                _timeManager.UnlockTickTime();
 
                // Invalidate the input devices on the InputManager 
                InputManager.UnsecureCurrent.InvalidateInputDevices();
 
                //
                // before we call Render we want to save the in Interlock state so we know
                // if we need to schedule another render or not.  This is because we want to render
                // while the UCE is working on rendering the previous frame. To do this we would like to get into 
                // a NotifyPresent, CommitChannel, Render pattern.  If the interlock state is not
                // "Waiting" at this point, then we must be in a Render, CommitChannel pattern 
                // and we will need to do something in order to get us back in parallel operation 
                //
 
                bool interlockWasNotWaiting = !InterlockIsWaiting;

                //
                // This is the big Render! 
                //
                // We've now updated timing and layout and the updated scene will be sent 
                // to the UCE. 
                //
 
                Render((ICompositionTarget)resizedCompositionTarget);

                //
                // We've processed the currentRenderOp so clear it 
                //
 
                if (_currentRenderOp != null) 
                {
                    _currentRenderOp.Abort(); 
                    _currentRenderOp = null;
                }

                if (!InterlockIsEnabled) 
                {
                    // 
                    // Schedule our next rendering operation. We want to introduce 
                    // a minimum delay, so that we don't overrun the composition
                    // thread 
                    //

                    ScheduleNextRenderOp(_timeDelay);
                } 
                else if (interlockWasNotWaiting)
                { 
                    // 
                    // schedule another render because we were in the Render, CommitChannel
                    // pattern, and this will get us back in the CommitChannel, Render pattern so 
                    // we will have the channel full when notification comes back from the UCE.
                    //

                    ScheduleNextRenderOp(TimeSpan.Zero); 
                }
 
                gotException = false; 
            }
            finally 
            {
                // Reset current operation so it can be re-queued by layout
                // This is needed when exception happens in the midst of layout/TemplateExpansion
                // and it unwinds from the stack. If we don't clean this field here, the subsequent 
                // PostRender won't queue new render operation and the window gets stuck. Bug 1355561.
                if (gotException 
                    && _currentRenderOp != null) 
                {
                    _currentRenderOp.Abort(); 
                    _currentRenderOp = null;
                }

                _isRendering = false; 
            }
        } 
 
        private int InvokeOnRenderCallbacksCount
        { 
            get
            {
                return _invokeOnRenderCallbacks != null ? _invokeOnRenderCallbacks.Count : 0;
            } 
        }
 
 
        /// 
        /// Calls all _invokeOnRenderCallbacks until no more are added 
        /// 
        private void FireInvokeOnRenderCallbacks()
        {
            int callbackLoopCount = 0; 
            int count = InvokeOnRenderCallbacksCount;
 
            // This outer loop is to re-run layout in case the app causes a layout to get enqueued in response 
            // to a Loaded event. In this case we would like to re-run layout before we allow render.
            do 
            {
                while (count > 0)
                {
                    callbackLoopCount++; 
                    if (callbackLoopCount > 153)
                    { 
                        throw new InvalidOperationException(SR.Get(SRID.MediaContext_InfiniteLayoutLoop)); 
                    }
 
                    FrugalObjectList callbacks = _invokeOnRenderCallbacks;
                    _invokeOnRenderCallbacks = null;

                    for (int i = 0; i < count; i++) 
                    {
                        callbacks[i].DoWork(); 
                    } 

                    count = InvokeOnRenderCallbacksCount; 
                }

                // Fire all the pending Loaded events before Render happens
                // but after the layout storm has subsided 
                FireLoadedPendingCallbacks();
 
                count = InvokeOnRenderCallbacksCount; 
            }
            while (count > 0); 
        }

        /// 
        /// Fire all the pending Loaded callbacks before Render happens 
        /// 
        private void FireLoadedPendingCallbacks() 
        { 
            // Fire all the pending Loaded events before Render happens but after layout
            if (_loadedOrUnloadedPendingOperations != null) 
            {
                var count = _loadedOrUnloadedPendingOperations.Count;
                if (count == 0)
                { 
                    return;
                } 
 
                // Create a copy of the _loadedOrUnloadedPendingOperations
                var copyOfPendingCallbacks = _loadedOrUnloadedPendingOperations; 

                // Clear up the _loadedOrUnloadedPendingOperations in case the broadcast of Loaded causes
                // more of the pending operations to get posted.
                _loadedOrUnloadedPendingOperations = null; 

                // Iterate and fire all the pending loaded operations 
                for (int i=0; i
        /// Render all registered ICompositionTargets. 
        ///  
        /// 
        /// * We have to render all visual targets on the same context at once. The reason for this is that 
        ///   we batch per Dispatcher (we use the context to get to the batch all over the place).
        /// * On a WM_SIZE we also need to render because USER32 is sitting in a tight loop sending us messages
        ///   continously. Hence we need to render all visual trees attached to visual targets otherwise we
        ///   would submit a batch that has only part of the changes for some visual trees. This would cause 
        ///   structural tearing.
        ///  
        private void Render(ICompositionTarget resizedCompositionTarget) 
        {
            // resizedCompositionTarget is the HwndTarget that is currently being resized. 

            //
            // Disable reentrancy during the Render pass.  This is because much work is done
            // during Render and we cannot survive reentrancy in these code paths. 
            // Disabling processing will prevent the lock() statement from pumping messages,
            // so we don�t run the risk of having to process an unrelated message in the middle 
            // of this code. Message pumping will resume sometime after we return. 
            //
            // Note: The possible downside of DisableProcessing is 
            //      1) Cross-Apartment COM calls may deadlock.
            //      2) We restrict what people can do in callbacks ie, they can�t display a message box.
            //
            using (Dispatcher.DisableProcessing()) 
            {
                Debug.Assert(CheckAccess()); 
 
                Debug.Assert(!_isDisposed);
                Debug.Assert(_registeredICompositionTargets != null); 

                // ETW event tracing
                bool etwTracingEnabled = false;
                uint renderID = (uint)Interlocked.Increment(ref _contextRenderID); 
                if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
                { 
                    etwTracingEnabled = true; 

                    DUCE.ETWEvent.RaiseEvent( 
                        _uceEtwEvent.Handle,
                        renderID,
                        Channel);
 
                    EventTrace.EventProvider.TraceEvent(
                        EventTrace.Event.WClientMediaRenderBegin, 
                        EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, 
                        renderID,
                        TicksToCounts(_estimatedNextPresentationTime.Ticks) 
                        );
                }

                // --------------------------------------------------------------- 
                // 1) Render each registered ICompositionTarget to finish up the batch.
 
                foreach (ICompositionTarget registeredTarget in _registeredICompositionTargets.Keys) 
                {
                    DUCE.ChannelSet channelSet; 
                    channelSet.Channel = _channelManager.Channel;
                    channelSet.OutOfBandChannel = _channelManager.OutOfBandChannel;
                    _currentRenderingChannel = channelSet;
                    registeredTarget.Render((registeredTarget == resizedCompositionTarget), channelSet.Channel); 
                    _currentRenderingChannel = null;
                } 
 

                // ---------------------------------------------------------------- 
                // 2) Update any resources that need to be updated for this render.

                RaiseResourcesUpdated();
 

                // 
                // 3) Commit the channel. 
                //
                // if we are not already waiting for a present then commit the 
                // channel at this time. If we are waiting for a present then we
                // will wait until we have presented before committing this channel
                //
 
                if (Channel != null)
                { 
                    Channel.CloseBatch(); 
                }
 
                _needToCommitChannel = true;
                _commitPendingAfterRender = true;
                if (!InterlockIsWaiting)
                { 
                    //if we've already commit during this vblank interval, dont do it again
                    // because it will cause the DWM to stall 
                    if (HasCommittedThisVBlankInterval) 
                    {
                        CommitChannelAfterNextVSync(); 
                    }
                    else
                    {
                        CommitChannel(); 
                    }
                } 
 
                // ---------------------------------------------------------------
                // 4) Raise RenderComplete event. 

                if (etwTracingEnabled)
                {
                    EventTrace.EventProvider.TraceEvent( 
                        EventTrace.Event.WClientMediaRenderEnd,
                        EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info); 
 

                    // trace the UI Response event 
                    EventTrace.EventProvider.TraceEvent(
                        EventTrace.Event.WClientUIResponse,
                        EventTrace.Keyword.KeywordGraphics, EventTrace.Level.Info,
                        GetHashCode(), 
                        renderID);
                } 
            } 
        }
 

        /// 
        /// Commit the current channel to the composition thread.
        ///  
        /// 
        /// This allows us to separate updating the visual tree from sending 
        /// data to the UCE. When in InterlockedPresentation mode, we'll always 
        /// have layout properly updated but we'll only commit 1 render per
        /// frame to the composition. 
        /// 
        private void CommitChannel()
        {
            // if we get render messages posted while we are disconnected we don't have a channel. 
            if (Channel != null)
            { 
                Debug.Assert(_needToCommitChannel, "CommitChannel called with nothing on the channel"); 

                if (InterlockIsEnabled) 
                {
                    Debug.Assert(!InterlockIsWaiting,
                        "We can't be committing the channel while waiting for a notification");
 
                    long currentTicks = CurrentTicks;
                    long presentationTime = _estimatedNextPresentationTime.Ticks; 
 
                    //
                    // it is possible that presentationTime is in the past, if we request this time 
                    // we will get an immediate NotifyPresent instead of getting one after the next
                    // vblank.  To prevent this we ensure the presentaitonTime is no earlier than the
                    // next VBlank time.
                    // 
                    if (_animationRenderRate > 0)
                    { 
                        long nextVBlank = currentTicks + TicksUntilNextVsync(currentTicks); 
                        if (nextVBlank > presentationTime)
                        { 
                            presentationTime = nextVBlank;
                        }
                    }
 
                    RequestPresentedNotification(Channel, TicksToCounts(presentationTime));
 
                    // 
                    // If we are in interlocked presentation mode then we enter
                    // a wait state once we commit the channel. 
                    //
                    _interlockState = InterlockState.WaitingForResponse;
                    _lastCommitTime = currentTicks;
                } 

                if (CommittingBatch != null) 
                { 
                    CommittingBatch(Channel, new EventArgs());
                } 

                Channel.Commit();

                if (_commitPendingAfterRender) 
                {
                    // 
                    // Raise Render Complete event since a Render happened and 
                    // the commit for that render happened.
                    // 
                    if (_renderCompleteHandlers != null)
                    {
                        _renderCompleteHandlers(this, null);
                    } 

                    _commitPendingAfterRender = false; 
                } 

                // 
                // The channel has just been commited. There's nothing more in it.
                //

                // The payload data for this event is the render ID of the frame we're committing. 
                EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordGraphics | EventTrace.Keyword.KeywordPerf, EventTrace.Event.WClientUICommitChannel, _contextRenderID);
 
            } 

            _needToCommitChannel = false; 
        }

        /// 
        /// Asks the composition engine to notify us once the frame we are 
        /// submitted has been presented to the screen.
        ///  
        ///  
        /// Critical        - Contains an unsafe code block.
        /// TreatAsSafe     - Unsafe block just uses the sizeof operator. 
        ///                   Sending a message to a channel is safe.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void RequestPresentedNotification(DUCE.Channel channel, long estimatedFrameTime) 
        {
            Debug.Assert(InterlockIsEnabled, 
                "Cannot request presentation notification unless interlock mode is enabled"); 

            unsafe 
            {
                DUCE.MILCMD_PARTITION_NOTIFYPRESENT data;
                data.Type = MILCMD.MilCmdPartitionNotifyPresent;
                data.FrameTime = (ulong) estimatedFrameTime; 

                channel.SendCommand( 
                    (byte*)&data, 
                    sizeof(DUCE.MILCMD_PARTITION_NOTIFYPRESENT),
                    true); 
            }
        }

        ///  
        /// CompleteRender
        ///  
        ///  
        /// Wait for the rendering loop to finish any pending instructions.
        ///  
        /// 
        /// Critical    - Calls one of two critical methods: WaitForNextMessage
        ///               or MilComposition_SyncFlush.
        /// TreatAsSafe - Net effect is to wait until render completes. Waiting 
        ///               is safe because we only block the thread if we know
        ///               we are waiting for a message. Flushing the channel is 
        ///               safe because we own the channel and we know there 
        ///               aren't unfinished batches at this point.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        internal void CompleteRender()
        {
            // for now just bail if we are not connected. 
            if (Channel != null)
            { 
                // 
                // In intelocked mode in order to make sure that frames are
                // fully updated to the screen we need to do the folloing: 
                // 1. If we are waiting for a response from the UCE, then wait
                //    until we get that response.
                // 2. If we had pending operations then flush the channel and
                //    wait for the notification that this new frame has been 
                //    processed by the composition.
                // 
                if (InterlockIsEnabled) 
                {
                    if (_interlockState == InterlockState.WaitingForResponse) 
                    {
                        do
                        {
                            // WaitForNextMessage will Commit() 
                            if (CommittingBatch != null)
                            { 
                                CommittingBatch(Channel, new EventArgs()); 
                            }
 
                            Channel.WaitForNextMessage();
                            NotifyChannelMessage();
                        } while (_interlockState == InterlockState.WaitingForResponse);
                    } 

                    // 
                    // We might have started a timer to wait for the next frame. 
                    // stop it now and go back to idle state
                    // 
                    _estimatedNextVSyncTimer.Stop();
                    _interlockState = InterlockState.Idle;

                    if (_needToCommitChannel) 
                    {
                        CommitChannel(); 
 
                        if (_interlockState == InterlockState.WaitingForResponse)
                        { 
                            do
                            {
                                // WaitForNextMessage will Commit(), but CommitChannel()
                                // was just called already and it handles CommittingBatch 
                                Channel.WaitForNextMessage();
                                NotifyChannelMessage(); 
                            } while (_interlockState == InterlockState.WaitingForResponse); 

                            // 
                            // We might have started a timer to wait for the next frame.
                            // stop it now and go back to idle state
                            //
                            _estimatedNextVSyncTimer.Stop(); 
                           _interlockState = InterlockState.Idle;
                        } 
                    } 
                }
                else 
                {
                    // SyncFlush() will Commit()
                    if (CommittingBatch != null)
                    { 
                        CommittingBatch(Channel, new EventArgs());
                    } 
 
                    //
                    // Issue a [....] flush, which will only return after 
                    // the last frame is presented
                    //

                    Channel.SyncFlush(); 
                }
            } 
        } 

        ///  
        /// This function is registered with the MediaContext's TimeManager. It is called whenever
        /// a clock managed by the TimeManager goes active, but only if there hasn't been already an
        /// active clock. For now we start the animation thread in there.
        ///  
        private void OnNeedTickSooner(object sender, EventArgs e)
        { 
            PostRender(); 
        }
 
        /// 
        /// Checks if the current context can request the specified permissions.
        /// 
        internal void VerifyWriteAccess() 
        {
            if (!WriteAccessEnabled) 
            { 
                throw new InvalidOperationException(SR.Get(SRID.MediaContext_APINotAllowed));
            } 
        }

        /// 
        /// Returns false if the MediaContext is currently read-only 
        /// 
        internal bool WriteAccessEnabled 
        { 
            get { return _readOnlyAccessCounter <= 0; }
        } 

        /// 
        /// Methods to lock down the Visual tree for write access.
        ///  
        internal void PushReadOnlyAccess()
        { 
            _readOnlyAccessCounter++; 
        }
 
        internal void PopReadOnlyAccess()
        {
            _readOnlyAccessCounter--;
        } 

        ///  
        /// Each MediaContext is associated with a TimeManager. The TimeManager is shared by all ICompositionTargets. 
        /// 
        private TimeManager _timeManager; 

        internal TimeManager TimeManager
        {
            get 
            {
                return _timeManager; 
            } 
        }
 

        /// 
        /// RenderComplete event is fired when Render method commits the channel.
        /// This is used for ink transition. Currently this event is internal and will 
        /// be accessed using reflection until proper object model is defined.
        ///  
        internal event EventHandler RenderComplete 
        {
            add 
            {
                //
                // If the Render happened (i.e. flag is true) and corresponding Commit
                // has not happened, then set the flag to false, since the next 
                // Commit will not have the changes after Render and before +RenderComplete.
                // In other words, consider this event sequence:- 
                //  1. Render 
                //  2. Some resource property changes (eg. visual.Clip = null)
                //  3. +RenderComplete 
                //  4. Commit
                // Then the 4th Commit will not have 2's changes(as it requires a
                // new Render pass) and so raising the event is wrong.
                // 
                // Note: If the event is added every time in the middle of Render
                // and Commit, then RenderComplete will starve. 
                // 
                if (_commitPendingAfterRender)
                { 
                    _commitPendingAfterRender = false;
                }
                _renderCompleteHandlers += value;
            } 
            remove
            { 
                _renderCompleteHandlers -= value; 
            }
        } 

        /// 
        /// ResourcesUpdatedHandler - This event handler prototype defines the callback
        /// for our async update callback in the ResourcesUpdated Event. 
        /// The method which implements this prototype is also often called in situations where
        /// the resource is known to be "on channel" - in those cases, "true" is passed for the second 
        /// parameter (allowing the implementation to skip the check). 
        /// 
        internal delegate void ResourcesUpdatedHandler(DUCE.Channel channel, bool skipOnChannelCheck); 

        internal event ResourcesUpdatedHandler ResourcesUpdated
        {
            add 
            {
                _resourcesUpdatedHandlers += value; 
            } 
            remove
            { 
                _resourcesUpdatedHandlers -= value;
            }
        }
 
        private void RaiseResourcesUpdated()
        { 
            if (_resourcesUpdatedHandlers != null) 
            {
                DUCE.ChannelSet channelSet = GetChannels(); 
                _resourcesUpdatedHandlers(channelSet.Channel, false /* do not skip the "on channel" check */);
                _resourcesUpdatedHandlers = null;
            }
        } 

        ///  
        /// Create a fresh or fetch one from the pool synchronous channel. 
        /// 
        internal DUCE.Channel AllocateSyncChannel() 
        {
            return _channelManager.AllocateSyncChannel();
        }
 
        /// 
        /// Returns a [....] channel back to the pool. 
        ///  
        internal void ReleaseSyncChannel(DUCE.Channel channel)
        { 
            _channelManager.ReleaseSyncChannel(channel);
        }

 
        /// 
        /// Returns the asynchronous channel for this media context. 
        ///  
        /// 
        /// This property is deprecated and scheduled to be removed as per task #26681. 
        /// Please do not create additional dependencies on it.
        /// 
        internal DUCE.Channel Channel
        { 
            get
            { 
                return _channelManager.Channel; 
            }
        } 

        /// 
        /// Returns the asynchronous out-of-band channel for this media context.
        ///  
        internal DUCE.Channel OutOfBandChannel
        { 
            get 
            {
                return _channelManager.OutOfBandChannel; 
            }
        }

 
        internal bool IsConnected
        { 
            get 
            {
                return _isConnected; 
            }
        }

        ///  
        /// Returns the BoundsDrawingContextWalker for this media context.
        /// To handle reentrance we want to make sure that 
        /// no one else on the same thread gets the same context. 
        /// 
        internal BoundsDrawingContextWalker AcquireBoundsDrawingContextWalker() 
        {
            if (_cachedBoundsDrawingContextWalker == null)
            {
                return new BoundsDrawingContextWalker(); 
            }
 
            BoundsDrawingContextWalker ctx = _cachedBoundsDrawingContextWalker; 
            _cachedBoundsDrawingContextWalker = null;
            ctx.ClearState(); 

            return ctx;
        }
 
        /// 
        /// Set the BoundsDrawingContextWalker for next use 
        /// To handle reentrance we want to make sure that 
        /// no one else on the same thread gets the same context.
        ///  
        internal void ReleaseBoundsDrawingContextWalker(BoundsDrawingContextWalker ctx)
        {
            _cachedBoundsDrawingContextWalker = ctx;
        } 

        private void PromoteRenderOpToInput(object sender, EventArgs e) 
        { 
            if(_currentRenderOp != null)
            { 
                _currentRenderOp.Priority = DispatcherPriority.Input;
            }

            ((DispatcherTimer)sender).Stop(); 
        }
 
        private void PromoteRenderOpToRender(object sender, EventArgs e) 
        {
            if(_currentRenderOp != null) 
            {
                _currentRenderOp.Priority = DispatcherPriority.Render;
            }
 
            ((DispatcherTimer)sender).Stop();
        } 
 
        /// 
        /// We setup a timer when the UCE doesn't present a frame because the 
        /// scene hasn't changed. When this timer triggers, we have passed the
        /// estimated time at which the predicted VSync should have occured.
        /// We can now commit our accumulated changes to the channel. This
        /// timer should only be active if we got a NoPresent notification from 
        /// the composition thread.
        ///  
 
        private void EstimatedNextVSyncTimeExpired(object sender, EventArgs e)
        { 
            Debug.Assert(_interlockState == InterlockState.WaitingForNextFrame
                         && _lastPresentationResults == MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_NOPRESENT,
                "CommitRenderChannel timer should only be trigger while waiting for the frame to expire");
 
            //
            // if we wake up before our earliest wakup time, we run the risk of commiting twice in one 
            // vblank interval.  This is mitigated by detecting the early wakup and creating a new timer 
            // for the remaining time.  This could cause us to skip a vblank if our new timer wakes up too
            // late, but this is seens as a better alternative to commiting twice in one vblank interval. 
            // A better solution would be to have some form of high resolution timer.
            //
            long currentTicks = CurrentTicks;
            DispatcherTimer timer = ((DispatcherTimer)sender); 
            long earliestWakeupTicks = 0;
            if(timer.Tag != null) 
                earliestWakeupTicks = (long)timer.Tag; 
            if (earliestWakeupTicks > currentTicks)
            { 
                timer.Stop();
                timer.Interval = TimeSpan.FromTicks(earliestWakeupTicks - currentTicks);
                timer.Start();
                return; 
            }
 
            _interlockState = InterlockState.Idle; 

            if (_needToCommitChannel) 
            {
                CommitChannel();

                //schedule the next render so we're back on the np-commit-render pattern 
                ScheduleNextRenderOp(TimeSpan.Zero);
            } 
 
            timer.Stop();
        } 

        //+----------------------------------------------------------------------
        //
        //  Private Methods 
        //
        //---------------------------------------------------------------------- 
 
        #region Private Methods
 
        /// 
        /// Tells the composition engine that we want to receive asynchronous
        /// notifications on this channel.
        ///  
        /// 
        /// Critical        - Contains an unsafe code block. 
        /// TreatAsSafe     - Unsafe block just uses the sizeof operator. 
        ///                   Sending a message to a channel is safe.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private void RegisterForNotifications(DUCE.Channel channel)
        {
 
            DUCE.MILCMD_PARTITION_REGISTERFORNOTIFICATIONS registerCmd;
 
            registerCmd.Type = MILCMD.MilCmdPartitionRegisterForNotifications; 
            registerCmd.Enable = 1;  // Enable notifications.
 
            unsafe
            {
                channel.SendCommand(
                    (byte*)®isterCmd, 
                    sizeof(DUCE.MILCMD_PARTITION_REGISTERFORNOTIFICATIONS)
                    ); 
            } 
        }
 
        #endregion Private Methods


        //+--------------------------------------------------------------------- 
        //
        //  Private Fields 
        // 
        //----------------------------------------------------------------------
 
        #region Private Fields
        /// 
        /// Returns the current channel set for this MediaContext.
        ///  
        internal DUCE.ChannelSet GetChannels()
        { 
            DUCE.ChannelSet channelSet; 
            channelSet.Channel = _channelManager.Channel;
            channelSet.OutOfBandChannel = _channelManager.OutOfBandChannel; 
            return channelSet;
        }

        ///  
        /// The disposed flag indicates if the object got disposed.
        ///  
        private bool _isDisposed; 

        ///  
        /// Event handler that is called when the context is destroyed.
        /// 
        private EventHandler _destroyHandler;
 
        /// 
        /// This event is raised when the MediaContext.Render has 
        /// committed the channel. 
        /// 
        private event EventHandler _renderCompleteHandlers; 

        /// 
        /// This event is raised when the MediaContext is ready for all of the
        /// invalid resources to update their values. 
        /// 
        private event ResourcesUpdatedHandler _resourcesUpdatedHandlers; 
 
        /// 
        /// Use a guid to uniquely identify the current context.  Note that 
        /// we can't use static data to generate a unique id since static
        /// data is not shared across app domains and this id must be
        /// truly unique.
        ///  
        private Guid _contextGuid;
 
        ///  
        /// Message delegate.
        ///  
        private DispatcherOperation _currentRenderOp;
        private DispatcherOperation _inputMarkerOp;
        private DispatcherOperationCallback _renderMessage;
        private DispatcherOperationCallback _animRenderMessage; 
        private DispatcherOperationCallback _inputMarkerMessage;
        private DispatcherOperationCallback _renderModeMessage; 
        private DispatcherTimer _promoteRenderOpToInput; 
        private DispatcherTimer _promoteRenderOpToRender;
 
        /// 
        /// This timer is used to keep track of when we should commit our
        /// accumulated renders to the composition thread when the composition
        /// thread hasn't presented our frame. If we didn't wait for this time 
        /// we might present a frame one vsync too early.
        ///  
        private DispatcherTimer _estimatedNextVSyncTimer; 

        ///  
        /// The channel manager is a security wrapper for channel operations.
        /// 
        private ChannelManager _channelManager;
 
        /// 
        /// ETW Event Resource handle for performance tracing 
        ///  
        private DUCE.Resource _uceEtwEvent = new DUCE.Resource();
 
        /// 
        /// Indicates that we are in the middle of processing a render message.
        /// 
        private bool _isRendering; 

        ///  
        /// Indicates we are in the process of disconnecting. 
        /// This flag is set so that we know not to schedule an animation
        /// render after the render pass we use to unmarshall resources. 
        /// 
        private bool _isDisconnecting = false;

        ///  
        /// Indicates we are in a disconnected state. This flag is used by the
        /// composition targets to determine if they need to DeleteCobsInSubgraph 
        /// (i.e. unmarshall the visual tree) 
        /// 
        private bool _isConnected = false; 

        private FrugalObjectList _invokeOnRenderCallbacks;

        ///  
        /// Set of ICompositionTargets that are currently registered with the MediaSystem;
        ///  
        private Dictionary _registeredICompositionTargets; 

        ///  
        /// This are the the permissions the Context has to access Visual APIs.
        /// 
        private int _readOnlyAccessCounter;
 
        private BoundsDrawingContextWalker _cachedBoundsDrawingContextWalker = new BoundsDrawingContextWalker();
 
        // 
        // The ID associated with a render dispatch.  This is used to track
        // renders, and is used by the realization cache to determine 
        // uniqueness of realizations, and across the UI and UCE threads
        // in ETW trace events.
        //
        private static int _contextRenderID = 0; 

        // The render tier associated with this MediaContext. This is updated 
        // when channels are created. 
        private int _tier;
 
        /// 
        /// Rendering event.  Registers a delegate to be notified after animation and layout but before rendering
        /// Its EventArgs parameter can be cast to RenderingEventArgs to get the last presentation time.
        ///  
        internal event EventHandler Rendering;
 
        ///  
        /// CommittingBatch event.  Registers a delegate to be notified when a batch is
        /// about to be committed to MIL. 
        /// 
        internal event EventHandler CommittingBatch;

        // List of pending loaded event dispatcher operations 
        private FrugalObjectList _loadedOrUnloadedPendingOperations;
 
        // Time to wait for unthrottled renders 
        private TimeSpan _timeDelay = TimeSpan.FromMilliseconds(10);
 
        // A flag to determine if RenderComplete event is raised. We only
        // raise the event if Render + Commit happens.
        //
        // Note: If the event is added every time in the middle of Render 
        // and Commit, then RenderComplete will starve.
        private bool _commitPendingAfterRender; 
 
        // The top-level hidden notification window that is used to receive
        // and forward broadcast messages 
        private MediaContextNotificationWindow _notificationWindow;

        private DUCE.ChannelSet? _currentRenderingChannel = null;
 
        #endregion Private Fields
 
 
        //+---------------------------------------------------------------------
        // 
        //  Animation Smoothing
        //
        //---------------------------------------------------------------------
 
        #region Animation Smoothing
 
        ///  
        /// This enum indicates that we are in an interlocked presentation mode.
        /// We render a frame. send it to be presented and the composition comes 
        /// to tell us that the frame was presented. Otherwise we just keep giving
        /// frames to the composition thread and assume that they get presented
        /// as requested.
        ///  

        private enum InterlockState 
        { 
            /// 
            /// Interlock presentation mode is disabled. We send a frame to the 
            /// composition thread and simply schedule our next one when we know
            /// that we have something to render
            /// 
            Disabled             = 0, 

            ///  
            /// Interlock presentation mode has requested a roundtrip message to 
            /// before enabling the mode. This state indicates that we've
            /// requested the UCE enter the interlocked presentation mode but we 
            /// haven't received the response yet.
            /// 
            RequestedStart,
 
            /// 
            /// We are in interlocked presentation mode and are not waiting for 
            /// anything. If we get a render request we will process it 
            /// immediately
            ///  
            Idle,

            /// 
            /// We are in interlocked presentation mode and have sumitted a 
            /// frame to be presented. We are waiting for the notification from
            /// the UCE thread. 
            ///  
            WaitingForResponse,
 
            /// 
            /// We are in interlocked presentation mode but the last frame we
            /// submitted to be presented wasn't presented. We are waiting until
            /// the next VSync before submitting another frame for presentation. 
            /// 
            WaitingForNextFrame 
        }; 

        ///  
        /// The current state of the interlocked presentation mode
        /// 
        private InterlockState _interlockState;
 
        /// 
        /// This indicates that we are waiting for something (either the next 
        /// frame time to occur or for a response from the UCE). 
        /// 
        private bool InterlockIsWaiting 
        {
            get
            {
                return (_interlockState == InterlockState.WaitingForNextFrame || 
                        _interlockState == InterlockState.WaitingForResponse);
            } 
        } 

        ///  
        /// We are currently in an interlocked presentation mode and the UCE
        /// has acknolwedged that it is also in that mode.
        /// 
        private bool InterlockIsEnabled 
        {
            get 
            { 
                return (   _interlockState != InterlockState.Disabled
                        && _interlockState != InterlockState.RequestedStart); 
            }
        }

        ///  
        /// This is used in interlocked presentation mode. This flag indicates
        /// that we've put something on the channel but that we haven't commited 
        /// it yet. This occurs when we know that the composition thread is already 
        /// processing a batch for a frame. We only want to give the UCE 1 frame
        /// per VSync so that we try to make it present the frame at the time 
        /// that we have estimated. At this point we have rendered 1 frame in
        /// advance and will wait until we reach the next frame boundary before
        /// committing the channel to have the information sent to the
        /// composition thread on the right frame. 
        /// 
        private bool _needToCommitChannel; 
 
        /// 
        /// Last time the composition presented a frame 
        /// Units: counts
        /// 
        private long _lastPresentationTime;
 
        /// 
        /// Last time the UI thread committed a frame 
        /// Units: Ticks 
        /// 
        private long _lastCommitTime; 

        /// 
        /// Last time the the input marker was added to the queue
        /// Units: Ticks 
        /// 
        private long _lastInputMarkerTime; 
 
        /// 
        /// Average time it takes the UCE to present a frame 
        /// Units: Ticks
        /// 
        private long _averagePresentationInterval;
 
        /// 
        /// Estimation of the next time we want a frame to appear on screen. We 
        /// will set the TimeManager's time to this to have the animations look 
        /// smooth
        ///  
        private TimeSpan _estimatedNextPresentationTime;

        /// 
        /// The refresh rate of the monitor that we are displaying to 
        /// 
        private int _displayRefreshRate; 
 
        /// 
        /// The rate at which we try to display content if no special throttling 
        /// mechanism is used
        /// 
        private int _adjustedRefreshRate;
 
        /// 
        /// The rate at which we are rendering animations. This can be the 
        /// refresh rate of the monitor that we are presenting on or can be 
        /// overridden with a DesiredFrameRate on the Timeline. This is
        /// used to estimate the time of the next frame that we want to present. 
        /// 
        private int _animationRenderRate;

        ///  
        /// The results of the last present call.
        ///  
        private MIL_PRESENTATION_RESULTS _lastPresentationResults = MIL_PRESENTATION_RESULTS.MIL_PRESENTATION_VSYNC_UNSUPPORTED; 

        static private long _perfCounterFreq; 

        private const long MaxTicksWithoutInput = TimeSpan.TicksPerSecond / 2;

        #endregion Animation Smoothing 
    }
 } 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK