MediaContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / Media / MediaContext.cs / 7 / 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 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;
            }

            if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
            {
                EventTrace.EventProvider.TraceEvent( 
                    EventTrace.GuidFromId(EventTraceGuidId.PERFFREQUENCYGUID), 
                    MS.Utility.EventType.Info,
                    _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();

            _connectMessage = new DispatcherOperationCallback(ConnectHandler);
            _disconnectMessage = new DispatcherOperationCallback(DisconnectHandler); 
            _transformHintUpdateMessage = new DispatcherOperationCallback(TransformHintUpdateHandler);
 
            // Create a hashtable in which we manage the CompositionTargets. 
            _registeredICompositionTargets = new System.Collections.Hashtable();
 
            _registeredLayeredWindows = new System.Collections.Hashtable();

            // 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; 

            // Create a notification window to listen for broadcast window messages
            _notificationWindow.CreateNotificationWindow(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);
 
            _bitmapEffectsUsed = false; 
            _commitPendingAfterRender = false;
        } 


        /// 
        /// Called by the notification window when WM_DWMCOMPOSITIONCHANGED is broadcasted. 
        /// 
        ///  
        /// Critical -- calls into an unsafe native method. 
        /// TreatAsSafe -- the call to DwmIsCompositionEnabled simply returns a boolean
        ///                through an out parameter, it is safe to be called in this context 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal void OnDWMCompositionChanged()
        { 
            // Check if the desktop composition is enabled.
            Int32 isDesktopCompositionEnabled = 0; 
            UnsafeNativeMethods.DwmIsCompositionEnabled(out isDesktopCompositionEnabled); 

            // Notify the mediasystem that it needs to re evaluate the transport generation id. 
            System.Windows.Media.MediaSystem.NotifyRedirectionEnvironmentChanged();

            // Iterate through the ICompositionTargets and let them know about
            // the DWM's composition changed broadcast. 
            foreach (DictionaryEntry entry in _registeredICompositionTargets)
            { 
                HwndTarget hwndTarget = (ICompositionTarget)entry.Key as HwndTarget; 

                if (hwndTarget != null) 
                {
                    hwndTarget.OnDWMCompositionChanged(isDesktopCompositionEnabled != 0);
                }
            } 
        }
 
 
        /// 
        /// 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 sync channels so that if the app handles the exception
                        // it will get a new partition on the next sync 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: 
                            NotifySetTier(message.Caps.Caps.TierValue); 
                            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; 
 
                        default:
                            HandleInvalidPacketNotification(); 
                            break;
                    }
                }
            } 
        }
 
        ///  
        /// NotifySetTier - this method is called to update the render tier.
        /// If the new render tier is different from the previous render tier, 
        /// this method will notify all listeners.
        /// 
        ///  int - the new render tier 
        private void NotifySetTier(int tier) 
        {
            if (_tier != tier) 
            { 
                _tier = tier;
 
                if (TierChanged != null)
                {
                    TierChanged(null, null);
                } 
            }
        } 
 
        /// 
        /// 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 three 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 UCEERR_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() 
        {
            //
            //
 

 
        } 

        ///  
        /// Tier Property - returns the current render tier for this MediaContext.
        /// 
        internal int Tier
        { 
            get
            { 
                return _tier; 
            }
        } 

        /// 
        /// 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(); 
                }
 
                //Dispatcher.WriteLineWithIndent("nextTickNeeded = {0}ms", nextTickNeeded.TotalMilliseconds);

                //
                // 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)
                    {
                        //Console.WriteLine("RenderMessageHandlerCore posting a new inactive queue item {0}", queueItemID++); 

                        //Dispatcher.WriteLineWithIndent("BeginInvoke at Inactive, set one timer"); 
                        //Dispatcher.Indent+=2; 

                        _currentRenderOp = Dispatcher.BeginInvoke(DispatcherPriority.Inactive, _animRenderMessage, null); 

                        _promoteRenderOpToRender.Interval = nextTickNeeded;
                        _promoteRenderOpToRender.Start();
 
                        //Dispatcher.WriteLineWithIndent(".");
                        //Dispatcher.Indent-=2; 
                    } 
                }
                // We need to tick soon (< 1 second) 
                else if (nextTickNeeded >= TimeSpan.Zero)
                {

                    //Dispatcher.WriteLineWithIndent("BeginInvoke at Inactive, set two timers"); 
                    //Dispatcher.Indent+=2;
 
                    // We need to tick in the future 
                    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();

                            // Debug.WriteLine(string.Format("{1}ms: ScheduleNextRenderOp: Posting a new queue item active in {0}ms", nextTickNeeded.TotalMilliseconds, CurrentTimeInMs); 
                        }
                    } 
                    else 
                    {
                        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(); 
                    } 

                    //Dispatcher.WriteLineWithIndent("."); 
                    //Dispatcher.Indent-=2;
                }
                else
                { 
                    //Console.WriteLine("ScheduleNextRenderOp not posting new queue item");
 
                    //Dispatcher.WriteLineWithIndent("Nothing to do"); 
                }
 
                //
                // Trace the scheduling of the render
                //
                if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
                {
                    EventTrace.EventProvider.TraceEvent( 
                        EventTrace.GuidFromId(EventTraceGuidId.SCHEDULERENDERGUID), 
                        MS.Utility.EventType.Info,
                        nextTickNeeded.TotalMilliseconds 
                        );
                }
            }
            //else if (_needToCommitChannel) 
            //{
                // Debug.WriteLine(string.Format("{0}: ScheduleNextRenderOp waiting to commit - didn't schedule next render", CurrentTimeInMs)); 
            //} 
        }
 
        /// 
        /// 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.
                // 
                //Console.WriteLine("Got a Presentation Notification {0}", presentationResults.ToString());

                //
                // 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;
 
 
                // Debug.WriteLine(String.Format("{0}: UCE Presented", CurrentTimeInMs));
 
                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(); 

                            // Debug.WriteLine({1}: String.Format("UCE returned early; waiting {0}ms until committing the channel", TicksUntilNextVsync(CurrentTicks) / TimeSpan.TicksPerMillisecond + 1, CurrentTimeInMs)); 
                        } 
                        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;
 
                            //if (_animationRenderRate > 0)
                            //{
                            //    Debug.WriteLine(String.Format("{1}: DWM presented for us and we waited till Vsync.  Last present time:, {0}ms", CountsToTicks(_lastPresentationTime) / TimeSpan.TicksPerMillisecond, CurrentTimeInMs));
                            //} 
                        }
                        break; 
                } 

                // Cap our Animation RenderRate to 1000 fps. 
                _animationRenderRate = Math.Min(_animationRenderRate, 1000);

                //
                // Trace the notification from the UCE 
                //
                if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
                { 
                    EventTrace.EventProvider.TraceEvent(
                        EventTrace.GuidFromId(EventTraceGuidId.UCENOTIFYPRESENTGUID), 
                        MS.Utility.EventType.Info,
                        _lastPresentationTime,          // The last presentation time
                        (Int64)presentationResults      // The presentation results
                        ); 
                }
 
                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");

                    // Debug.WriteLine(String.Format("{0}: UCE returned from Vsync; committing now.  Scheduling next render with a delay of {1}ms", CurrentTimeInMs, presentationDelay.TotalMilliseconds)); 
                }
 
                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).
        ///  
        private 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 sync 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. 
                    //
 
                    //Console.WriteLine("Entered interlockedMode");

                    _interlockState = InterlockState.Idle;
 
                    if (Channel != null)
                    { 
                        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
                        { 
                            // Debug.WriteLine(String.Format("{0}, CurrentTime kept the old frame estimate, {1}", CurrentTimeInMs, _estimatedNextPresentationTime));
                        } 
                    } 
                    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() 
        {
            bool layeredWindows = false; 
            if (_registeredLayeredWindows != null)
            {
                layeredWindows = _registeredLayeredWindows.Count != 0;
            } 
            _channelManager.CreateChannels(layeredWindows);
 
            if (_notificationHandler != null) 
            {
                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(Channel, DUCE.ResourceType.TYPE_ETWEVENTRESOURCE); 
 
            // Send a request for an updated render tier value
            RequestTier(Channel); 

            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();

            _primaryChannelRemote = (Channel.MarshalType == ChannelMarshalType.ChannelMarshalTypeCrossMachine);
        } 

        ///  
        /// 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)); 
 
                        _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));
 
                    // 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); 
            Debug.Assert(_notificationHandler != 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. 
            // 
            HwndTarget.SetChannelNotificationWindow(
                Channel, 
                _notificationHandler
                );

            // 
            // This actually populates the channel into the composition
            // engine so that it can receive notifications. 
            // 
            RegisterForNotifications(Channel);
 
            EnterInterlockedPresentation();
        }

        ///  
        /// 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 Hashtable contents, because ICompositionTarget.Dispose modifies this Hashtable.
                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 HashTable. This is why we don't iterate the HashTable directly. 
                foreach (ICompositionTarget iv in registeredVTs)
                { 
                    iv.Dispose();
                }
                _registeredICompositionTargets = null;
 
                // Dispose the notification window
                _notificationWindow.DisposeNotificationWindow(); 
 
                // 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(); 
                _timeManager = null; 

                // 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);
            }
        } 

        ///  
        /// 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.
                List targetChannels = (_currentRenderingChannel.Count == 0) ? GetChannels(iv) : _currentRenderingChannel; 
                foreach (DUCE.ChannelSet channelSet in targetChannels)
                { 
                    iv.AddRefOnChannel(channelSet.Channel, channelSet.OutOfBandChannel); 
                }
            } 

            // if we have _tokensForRegistration we are registering a visual brush from
            // within a render call. We need to defer adding this ICompositionTarget.
            if ( _tokensForRegistration != null) 
            {
                int count = _tokensForUnRegistration.Count; 
                Debug.Assert(!_tokensForRegistration.Contains(iv)); 
                _tokensForUnRegistration.Remove(iv);
 
                // if we had to remove the target from the unregister array we already have
                // it registered so do nothing. If the count before and after the remove are
                // the same we did not first un register so we need to add it in.
                if (count == _tokensForUnRegistration.Count) 
                {
                    _tokensForRegistration.Add(iv); 
                } 
            }
            else 
            {
                _registeredICompositionTargets.Add(iv, null); // We use the hashtable just as a set.
            }
 
            // When we get our first HwndTarget we may be able to start
            // processing notifications 
            if (_notificationHandler == null) 
            {
                HwndTarget target = iv as HwndTarget; 

                if (target != null)
                {
                    _notificationHandler = target; 

                    if (Channel != null) 
                    { 
                        HookNotifications();
                    } 
                }
            }
        }
 
        /// 
        /// 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.
                List targetChannels = (_currentRenderingChannel.Count == 0) ? GetChannels(iv) : _currentRenderingChannel; 
                foreach (DUCE.ChannelSet channelSet in targetChannels) 
                {
                    iv.ReleaseOnChannel(channelSet.Channel, channelSet.OutOfBandChannel); 
                }
            }

            // if we have _tokensForUnRegistration we are unregistering a visual brush from 
            // within a render call. We need to defer removing this ICompositionTarget.
            if ( _tokensForUnRegistration != null) 
            { 
                Debug.Assert(!_tokensForUnRegistration.Contains(iv));
                _tokensForUnRegistration.Add(iv); 
                _tokensForRegistration.Remove(iv);
            }
            else
            { 
                _registeredICompositionTargets.Remove(iv);
            } 
 
            //
            // If we remove the HwndTarget that we were using to handle 
            // notifications then we may not be able to process them anymore.
            // We need to release this one and look for a new one.
            //
 
            if (_notificationHandler != null)
            { 
                if (iv == _notificationHandler) 
                {
                    _notificationHandler = null; 
                    FindNotificationHandler();

                    if (Channel != null)
                    { 
                        if (_notificationHandler != null)
                        { 
                            HookNotifications(); 
                        }
                        else 
                        {
                            Channel.SetNotificationWindow(IntPtr.Zero, 0);

                            // 
                            // If we can't get notifications then we can't be in
                            // interlocked presentation mode. 
                            // 

                            LeaveInterlockedPresentation(); 
                        }
                    }
                }
            } 
        }
 
        ///  
        /// Ensures that we have a handler for back-channel message notifications
        /// if possible. 
        /// 
        private void FindNotificationHandler()
        {
            Debug.Assert(_notificationHandler == null); 

            if (_registeredICompositionTargets != null) 
            { 
                foreach (DictionaryEntry entry in _registeredICompositionTargets)
                { 
                    _notificationHandler = (ICompositionTarget)entry.Key as HwndTarget;
                    if (_notificationHandler != null)
                    {
                        // Found it. 
                        break;
                    } 
                } 
            }
        } 

        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 ArrayList(); 
            }
 
            _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 (_waitingForPresent)
            //{ 
            //    Console.WriteLine("PostRender called while waiting for a previous present");
            //} 
 
            //Dispatcher.WriteLineWithIndent("PostRender...");
            //Dispatcher.Indent+=2; 

            if (!_isRendering)
            {
                EventTrace.NormalTraceEvent(EventTraceGuidId.POSTRENDERGUID, MS.Utility.EventType.Info); 

                if (_currentRenderOp != null) 
                { 
                    //Console.WriteLine("PostRender promoting existing queue item");
 
                    //Dispatcher.WriteLineWithIndent("Promoting to Render...");
                    //Dispatcher.Indent+=2;

                    // 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; 
 
                    //Dispatcher.WriteLineWithIndent(".");
                    //Dispatcher.Indent-=2; 
                }
                else
                {
                    //Console.WriteLine("PostRender issuing new queue item"); 

                    //Dispatcher.WriteLineWithIndent("BeginInvoke at Render..."); 
                    //Dispatcher.Indent+=2; 

                    // If we don't have a render operation in the queue, add one at 
                    // render priority.
                    _currentRenderOp = Dispatcher.BeginInvoke(DispatcherPriority.Render, _renderMessage, null);

                    //Dispatcher.WriteLineWithIndent("."); 
                    //Dispatcher.Indent-=2;
                } 
 
                //Dispatcher.WriteLineWithIndent("Killing the timers...");
                //Dispatcher.Indent+=2; 

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

                //Dispatcher.WriteLineWithIndent("."); 
                //Dispatcher.Indent-=2; 
            }
            else 
            {
                //Console.WriteLine("PostRender called while rendering");

                //Dispatcher.WriteLineWithIndent("Already rendering!"); 
            }
 
            //Dispatcher.WriteLineWithIndent("."); 
            //Dispatcher.Indent-=2;
        } 

        /// 
        /// 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); 
        }
 
        internal void PostConnect()
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, _connectMessage, null);
        } 

        internal void PostDisconnect() 
        { 
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, _disconnectMessage, null);
        } 

        internal void PostTransformHintUpdate()
        {
            Dispatcher.BeginInvoke(DispatcherPriority.Render, _transformHintUpdateMessage, null); 
        }
 
        internal object ConnectHandler( 
            object obj
            ) 
        {
            // create all media context channels
            if (MediaSystem.ConnectChannels(this))
            { 
                _isConnected = true;
 
 
                ArrayList targets = new ArrayList();
                foreach (DictionaryEntry entry in _registeredICompositionTargets) 
                {
                    ICompositionTarget iv = ((ICompositionTarget)(entry.Key));
                    targets.Add(iv);
                } 

                foreach (ICompositionTarget iv in targets) 
                { 
                    List targetChannels = GetChannels(iv);
                    foreach (DUCE.ChannelSet channelSet in targetChannels) 
                    {
                        iv.AddRefOnChannel(channelSet.Channel, channelSet.OutOfBandChannel);
                    }
                    Resize(iv); 
                }
            } 
 
            return null;
        } 

        internal object DisconnectHandler(
            object obj
            ) 
        {
            // 
            // When we disconnect we need to exit interlocked mode 
            //
 
            LeaveInterlockedPresentation();

            ArrayList targets = new ArrayList();
 
            _isDisconnecting = true;
            _isConnected = false; 
 
            //
            // In order to ensure correctly ordered processing of all queued 
            // RemoveChild and Release commands, we need to flush these queues
            // for all channels here. If we don't, we can arrive at a situation
            // in which there are RemoveAndReleaseCommands queued for the root visual
            // of a CompositionTarget which has already been released from the channel 
            // the below calls to ICompositionTarget.ReleaseOnChannel(). This can
            // occur when we get a style changing message followed by a disconnect 
            // message, but could potentially occur in other situations where 
            // we modify the tree and then don't render before processing a
            // disconnect message. 
            //
            _channelManager.FlushAllRemoveReleaseQueues();

            foreach (DictionaryEntry entry in _registeredICompositionTargets) 
            {
                ICompositionTarget iv = ((ICompositionTarget)(entry.Key)); 
                targets.Add(iv); 
            }
 
            foreach (ICompositionTarget iv in targets)
            {
                List targetChannels = GetChannels(iv);
                foreach (DUCE.ChannelSet channelSet in targetChannels) 
                {
                    iv.ReleaseOnChannel(channelSet.Channel, channelSet.OutOfBandChannel); 
                } 
            }
            _isDisconnecting = false; 
            // shutdown all media context channels.
            RemoveChannels();
            return null;
        } 

        internal object TransformHintUpdateHandler( 
            object obj 
            )
        { 
            // Copy the updated list of transfrom hints from MediaSystem
            MediaSystem.UpdateTransformHints(this);

            // Force realization update on each composition target registered 
            // for this media context.
            ArrayList targets = new ArrayList(); 
 
            foreach (DictionaryEntry entry in _registeredICompositionTargets)
            { 
                ICompositionTarget iv = ((ICompositionTarget)(entry.Key));
                targets.Add(iv);
            }
 
            foreach (ICompositionTarget iv in targets)
            { 
                Resize(iv); 
            }
 
            return null;
        }

        ///  
        /// This is the standard RenderMessageHandler callback, posted via PostRender()
        /// and Resize().  This wraps RenderMessageHandlerCore and emits an ETW events 
        /// to trace its execution. 
        /// 
        internal object RenderMessageHandler( 
              object resizedCompositionTarget /* can be null if we are not resizing*/
            )
        {
            if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
            {
                EventTrace.EventProvider.TraceEvent( 
                    EventTrace.GuidFromId(EventTraceGuidId.RENDERHANDLERGUID), 
                    MS.Utility.EventType.StartEvent,
                    Dispatcher.GetHashCode()); 
            }

            //Console.WriteLine(string.Format("RenderMessageHandler {0}", queueItemID++));
 
            RenderMessageHandlerCore(resizedCompositionTarget);
 
            EventTrace.NormalTraceEvent(EventTraceGuidId.RENDERHANDLERGUID, MS.Utility.EventType.EndEvent); 

            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.
        ///  

        internal object AnimatedRenderMessageHandler(
            object resizedCompositionTarget /* can be null if we are not resizing*/
            ) 
        {
            if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
            { 
                 EventTrace.EventProvider.TraceEvent(
                    EventTrace.GuidFromId(EventTraceGuidId.ANIMRENDERHANDLERGUID), 
                    MS.Utility.EventType.StartEvent,
                    Dispatcher.GetHashCode());
            }
 

            RenderMessageHandlerCore(resizedCompositionTarget); 
 
            EventTrace.NormalTraceEvent(EventTraceGuidId.ANIMRENDERHANDLERGUID, MS.Utility.EventType.EndEvent);
 
            return null;

        }
 
        /// 
        /// This handles the _inputMarkerOp message.  We're using 
        /// _inputMarkerOp to determine if input priority dispatcher ops 
        /// have been processes.
        ///  
        internal 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;
            } 
        } 

 
        /// 
        /// Calls all _invokeOnRenderCallbacks until no more are added
        /// 
        private void FireInvokeOnRenderCallbacks() 
        {
            int callbackLoopCount = 0; 
            int count = _invokeOnRenderCallbacks != null ? _invokeOnRenderCallbacks.Count : 0; 

            // 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)); 
                    }

                    InvokeOnRenderCallback[] callbacks = new InvokeOnRenderCallback[count];
 
                    _invokeOnRenderCallbacks.CopyTo(callbacks);
                    _invokeOnRenderCallbacks.Clear(); 
 
                    for (int i = 0; i < count; i++)
                    { 
                        callbacks[i].DoWork();
                    }

                    count = _invokeOnRenderCallbacks.Count; 
                }
 
                // Fire all the pending Loaded events before Render happens 
                // but after the layout storm has subsided
                FireLoadedPendingCallbacks(); 

                count = _invokeOnRenderCallbacks != null ? _invokeOnRenderCallbacks.Count : 0;
            }
            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 && _loadedOrUnloadedPendingOperations.Count > 0) 
            {
                // Create a copy of the _loadedOrUnloadedPendingOperations 
                LoadedOrUnloadedOperation[] copyOfPendingCallbacks = new LoadedOrUnloadedOperation[_loadedOrUnloadedPendingOperations.Count]; 
                _loadedOrUnloadedPendingOperations.CopyTo(copyOfPendingCallbacks, 0);
 
                // Clear up the _loadedOrUnloadedPendingOperations in case the broadcast of Loaded causes
                // more of the pending operations to get posted.
                _loadedOrUnloadedPendingOperations.Clear();
 
                // 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. 
        /// 
        internal 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);
                Debug.Assert(_tokensForRegistration == null); 

                bool anyTreeContainsGraphness = false;

                RemoveChannel(_channelManager.Channel); 
                RemoveChannel(_channelManager.LocalAsynchronousChannel);
 
                _tokensForRegistration = new ArrayList(); 
                _tokensForUnRegistration = new ArrayList();
 
                // ETW event tracing
                bool etwTracingEnabled = false;
                uint renderID = (uint)Interlocked.Increment(ref _contextRenderID);
                if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal)) 
                {
                    etwTracingEnabled = true; 
 
                    DUCE.ETWEvent.RaiseEvent(
                        _uceEtwEvent.Handle, 
                        renderID,
                        Channel);

                    EventTrace.EventProvider.TraceEvent( 
                        EventTrace.GuidFromId(EventTraceGuidId.MEDIARENDERGUID),
                        MS.Utility.EventType.StartEvent, 
                        renderID, 
                        TicksToCounts(_estimatedNextPresentationTime.Ticks)
                        ); 
                }

                // ---------------------------------------------------------------
                // 1) Render each registered ICompositionTarget to finish up the batch. 

                foreach (DictionaryEntry entry in _registeredICompositionTargets) 
                { 
                    ICompositionTarget registeredTarget = ((ICompositionTarget)(entry.Key));
 
                    DUCE.ChannelSet channelSet;
                    channelSet.Channel = _channelManager.Channel;
                    channelSet.OutOfBandChannel = _channelManager.OutOfBandChannel;
                    _currentRenderingChannel.Add(channelSet); 
                    registeredTarget.Render((registeredTarget == resizedCompositionTarget), channelSet.Channel);
                    _currentRenderingChannel.Clear(); 
 
                    if (_registeredLayeredWindows.Contains(registeredTarget) && _channelManager.LocalAsynchronousChannel != null)
                    { 
                        channelSet.Channel = _channelManager.LocalAsynchronousChannel;
                        channelSet.OutOfBandChannel = _channelManager.LocalAsynchronousOutOfBandChannel;
                        _currentRenderingChannel.Add(channelSet);
                        registeredTarget.Render((registeredTarget == resizedCompositionTarget), channelSet.Channel); 
                        _currentRenderingChannel.Clear();
                    } 
 
                    anyTreeContainsGraphness |= registeredTarget.VisualTreeContainsGraphness();
                } 

                // process all the visual brush registration requests we picked up during the render walk.
                foreach (ICompositionTarget iv in _tokensForRegistration)
                { 
                    _registeredICompositionTargets.Add(iv, null); // We use the hashtable just as a set.
                } 
 
                // process all the visual brush unregistration requests we picked up during the render walk.
                foreach (ICompositionTarget iv in _tokensForUnRegistration) 
                {
                    _registeredICompositionTargets.Remove(iv);
                }
 
                _tokensForRegistration = null;
                _tokensForUnRegistration = null; 
 

                if (IsConnected) 
                {
                    // ----------------------------------------------------------------
                    // 2) Get the cached realization context if possible, otherwise
                    // create a new one. Note that we null out the cached context 
                    // field. This means that in failure cases we will always
                    // recreate the realization context. 
                    // 
                    //
 



 

                    if (etwTracingEnabled) 
                    { 
                        EventTrace.EventProvider.TraceEvent(
                            EventTrace.GuidFromId(EventTraceGuidId.UPDATEREALIZATIONSGUID), 
                            MS.Utility.EventType.StartEvent);
                    }

                    RealizationContext realizationContext = null; 

                    if (_cachedRealizationContext != null) 
                    { 
                        realizationContext = _cachedRealizationContext;
                        _cachedRealizationContext = null; 
                    }
                    else
                    {
                        realizationContext = new RealizationContext(); 
                    }
 
 
                    // ---------------------------------------------------------------
                    // 3) Prepare the realization context. 

                    //
                    // Can only walk incrementally if we haven't encountered graphness in the tree
                    // and don't have any transform hints. Transform hints can be considered another 
                    // form of graphness for text because they produce multiple realizations for
                    // each glyphrun 
                    // 
                    anyTreeContainsGraphness |= (_transformHints.Count > 0);
 
                    realizationContext.BeginFrame(!anyTreeContainsGraphness /* full walk */,
                                                  false /* walk for BitmapRenderTarget */);

                    // ---------------------------------------------------------------- 
                    // 4) Mark visible realizations for top-level visual targets.
 
                    foreach (DictionaryEntry entry in _registeredICompositionTargets) 
                    {
                        CompositionTarget vt = ((ICompositionTarget)(entry.Key)) as CompositionTarget; 

                        if (vt != null)
                        {
                            List targetChannels = GetChannels(vt); 
                            realizationContext.Channels = targetChannels;
 
                            realizationContext.WindowClip = vt.WorldClipBounds; 
                            vt.MarkVisibleRealizations(realizationContext);
 
                        }
                    }

                    // ---------------------------------------------------------------- 
                    // 5) Execute the scheduled realization updates.
 
                    realizationContext.ExecuteRealizationsUpdateSchedule(); 

                    // ----------------------------------------------------------- 
                    // 6) Now cache the realization context.

                    // Note that EndFrame call is required for caching the
                    // realization context. However, before calling endframe all 
                    // realization context stacks must be empty. If they are not
                    // empty at this point our code has a serious bug. 
 
                    realizationContext.EndFrame();
 
                    Debug.Assert(_cachedRealizationContext == null);

                    _cachedRealizationContext = realizationContext;
 

                    if (etwTracingEnabled) 
                    { 
                        EventTrace.EventProvider.TraceEvent(
                            EventTrace.GuidFromId(EventTraceGuidId.UPDATEREALIZATIONSGUID), 
                            MS.Utility.EventType.EndEvent);
                    }
                }
 

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

                RaiseResourcesUpdated(); 


                //
                // 8) 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
                // 

                _needToCommitChannel = true;
                _commitPendingAfterRender = true;
                if (!InterlockIsWaiting) 
                {
                    // Debug.WriteLine(string.Format("{0}, Committing the channel after render", CurrentTimeInMs)); 
 
                    //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(); 
                    }
                } 

                // ---------------------------------------------------------------
                // 9) Raise RenderComplete event.
 
                if (etwTracingEnabled)
                { 
                    EventTrace.EventProvider.TraceEvent( 
                        EventTrace.GuidFromId(EventTraceGuidId.MEDIARENDERGUID),
                        MS.Utility.EventType.EndEvent 
                        );


                    // trace the UI Response event 
                    EventTrace.EventProvider.TraceEvent(
                        EventTrace.GuidFromId(EventTraceGuidId.UIRESPONSEGUID), 
                        MS.Utility.EventType.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) 
                {
                    //Console.WriteLine("Requesting Presentation Notification"); 
                    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;
                } 

                //Console.WriteLine("Committing Channel");

                Channel.Commit(); 
                DUCE.Channel localAsyncChannel = _channelManager.LocalAsynchronousChannel;
                if (localAsyncChannel != null) 
                { 
                    localAsyncChannel.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.
                // 

                if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.normal))
                {
                    // The payload data for this event is the render ID of the frame we're committing. 
                    EventTrace.EventProvider.TraceEvent(
                        EventTrace.GuidFromId(EventTraceGuidId.COMMITCHANNELGUID), 
                        MS.Utility.EventType.Info, 
                        _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) 
                    );
            } 
        } 

        ///  
        /// 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)
                { 
                    //Console.WriteLine("CompleteRender: We are in InterlockedMode: {0}", _interlockState.ToString());
                    if (_interlockState == InterlockState.WaitingForResponse) 
                    { 
                        //Console.WriteLine("CompleteRender waiting for previous present");
                        do 
                        {
                            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)
                    { 
                        //Console.WriteLine("CompleteRender: committing channel"); 
                        CommitChannel();
 
                        if (_interlockState == InterlockState.WaitingForResponse)
                        {
                            //Console.WriteLine("CompleteRender waiting for present");
                            do 
                            {
                                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
                {
                    //Console.WriteLine("CompleteRender waiting for sync flush");
 
                    //
                    // Issue a sync 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) 
            {
                // 
                // We must do RaiseResourceUpdated for all channels 
                // since we don't know here which channels the specific resources
                // are on, we just iterate for all channels, and the resource 
                // must check which channels it is on (thus pass false for
                // skip on channel check flag)
                //
                List channels = GetAllChannels(); 
                foreach (DUCE.ChannelSet channelSet in channels)
                { 
                    DUCE.Channel channel = channelSet.Channel; 
                    _resourcesUpdatedHandlers(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 sync 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;
        } 

        ///  
        /// Describes whether any bitmap effects have been 
        /// used in the scene. This value can not be unset,
        /// and is used to propagate realization updates when 
        /// offset changes occur.
        /// 
        internal bool BitmapEffectsUsed
        { 
            get
            { 
                return _bitmapEffectsUsed; 
            }
            set 
            {
                _bitmapEffectsUsed |= value;
            }
        } 

        private void PromoteRenderOpToInput(object sender, EventArgs e) 
        { 
            //Dispatcher.WriteLineWithIndent("PromoteRenderOpToInput...");
            //Dispatcher.Indent+=2; 

            // Debug.WriteLine(String.Format("{0}, Promoting render operation to input", CurrentTimeInMs));

            if(_currentRenderOp != null) 
            {
                //Dispatcher.WriteLineWithIndent("Promoting to Input..."); 
                //Dispatcher.Indent+=2; 

                _currentRenderOp.Priority = DispatcherPriority.Input; 

                //Dispatcher.WriteLineWithIndent(".");
                //Dispatcher.Indent-=2;
            } 
            else
            { 
                //Dispatcher.WriteLineWithIndent("Nothing to promote"); 
            }
 
            ((DispatcherTimer)sender).Stop();

            //Dispatcher.WriteLineWithIndent(".");
            //Dispatcher.Indent-=2; 
        }
 
        private void PromoteRenderOpToRender(object sender, EventArgs e) 
        {
            //Dispatcher.WriteLineWithIndent("PromoteRenderOpToRender..."); 
            //Dispatcher.Indent+=2;

            if(_currentRenderOp != null)
            { 
                //Dispatcher.WriteLineWithIndent("Promoting to Render...");
                //Dispatcher.Indent+=2; 
 
                _currentRenderOp.Priority = DispatcherPriority.Render;
 
                //Dispatcher.WriteLineWithIndent(".");
                //Dispatcher.Indent-=2;
            }
            else 
            {
                //Dispatcher.WriteLineWithIndent("Nothing to promote"); 
            } 

            ((DispatcherTimer)sender).Stop(); 

            //Dispatcher.WriteLineWithIndent(".");
            //Dispatcher.Indent-=2;
        } 

        ///  
        /// 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) 
        { 
            //Dispatcher.WriteLineWithIndent("CommitRenderChannel...");
            //Dispatcher.Indent+=2; 

            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)
            {
                //Dispatcher.WriteLineWithIndent("Committing the current channel...");
                //Dispatcher.Indent+=2; 

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

                // Debug.WriteLine(String.Format("{0}, Next Vsync timer expired; commit the channel", CurrentTimeInMs));

                //Dispatcher.WriteLineWithIndent("."); 
                //Dispatcher.Indent-=2;
            } 
 
            timer.Stop();
 
           // Console.WriteLine("Timer expired. Commited Render");

            //Dispatcher.WriteLineWithIndent(".");
            //Dispatcher.Indent-=2; 
        }
 
        internal ArrayList TransformHints 
        {
            get { return _transformHints; } 
        }

        //+---------------------------------------------------------------------
        // 
        //  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

        ///  
        /// Registers an ICompositionTarget as a layered window or being displayed
        /// on another ICompositionTarget which is layered. This information is used 
        /// to correctly marshal the window and its content to the local asynchronous 
        /// compositor in the remote session case.
        ///  
        /// The ICompositionTarget to register
        internal bool AddLayeredWindow(ICompositionTarget ct)
        {
            // 
            // If running in a remote session, need to AddRef the
            // composition target 
            // 
            if (_primaryChannelRemote)
            { 
                //
                // May need to request a local channel if one doesn't exist yet
                // (eg this is the first time a layered window has been added
                // 
                if (_channelManager.LocalAsynchronousChannel == null)
                { 
                    _channelManager.CreateLocalAsynchronousChannels(); 
                }
                ct.AddRefOnChannel(_channelManager.LocalAsynchronousChannel, _channelManager.LocalAsynchronousOutOfBandChannel); 
            }

            //
            // Regardless of whether this is a remote session, 
            // register the window in our list as layered, because
            // someone could connect from a remote session at any time. 
            // 
            if (!_registeredLayeredWindows.Contains(ct))
            { 
                _registeredLayeredWindows.Add(ct, null);
            }

            return true; 
        }
 
        ///  
        /// Unregisters an ICompositionTarget as a layered window or being displayed
        /// on another ICompositionTarget which is layered. This information is used 
        /// to correctly marshal the window and its content to the local asynchronous
        /// compositor in the remote session case.
        /// 
        /// The ICompositionTarget to unregister 
        internal bool RemoveLayeredWindow(ICompositionTarget ct)
        { 
            if (_registeredLayeredWindows.Contains(ct)) 
            {
                _registeredLayeredWindows.Remove(ct); 
                //
                // Found the ICompositionTarget
                // Release on channel if we're connected in a remote
                // session 
                //
                if (_primaryChannelRemote) 
                { 
                    ct.ReleaseOnChannel(_channelManager.LocalAsynchronousChannel, _channelManager.LocalAsynchronousOutOfBandChannel);
                    // 
                    // Note we don't check to see if the channel is no longer
                    // used and delete it, and this is deliberate. Keeping the channel
                    // around is not expensive, and we may use it again if another
                    // layered window is created. 
                    //
                } 
                return true; 
            }
            else 
            {
                //
                // Couldn't find the ICompositionTarget, this is
                // not a serious error in itself, but could indicate 
                // failure to correctly call AddLayeredWindow
                // 
                return false; 
            }
        } 

        /// 
        /// Helper to create the ChannelSets
        ///  
        /// Specifies whether to add the local asynchronous channel set (if it exists for that ICompositionTarget)
        private List GetChannelsHelper(bool addLocalChannels) 
        { 
            List channels = new List();
            DUCE.ChannelSet channelSet; 
            channelSet.Channel = _channelManager.Channel;
            channelSet.OutOfBandChannel = _channelManager.OutOfBandChannel;
            channels.Add(channelSet);
 
            if (addLocalChannels && (_channelManager.LocalAsynchronousChannel != null))
            { 
                DUCE.ChannelSet localChannels; 
                localChannels.Channel = _channelManager.LocalAsynchronousChannel;
                localChannels.OutOfBandChannel = _channelManager.LocalAsynchronousOutOfBandChannel; 
                channels.Add(localChannels);
            }
            return channels;
        } 

        ///  
        /// Gets the current sets of channels which a particular ICompositionTarget is connected to 
        /// 
        /// ICompositionTarget to get channels for. 
        internal List GetChannels(ICompositionTarget ct)
        {
            return GetChannelsHelper(_registeredLayeredWindows.Contains(ct));
        } 

        ///  
        /// Gets all the current sets of channels in use by this MediaContext 
        /// 
        private List GetAllChannels() 
        {
            return GetChannelsHelper(true);
        }
 
        /// 
        /// 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 _connectMessage; 
        private DispatcherOperationCallback _disconnectMessage;
        private DispatcherOperationCallback _transformHintUpdateMessage; 
        private DispatcherOperationCallback _renderMessage; 
        private DispatcherOperationCallback _animRenderMessage;
        private DispatcherOperationCallback _inputMarkerMessage; 
        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 ArrayList _invokeOnRenderCallbacks;
 
        ///  
        /// ArrayList of ICompositionTargets that are currently registered with the MediaSystem;
        ///  
        private System.Collections.Hashtable _registeredICompositionTargets;

        private System.Collections.Hashtable _registeredLayeredWindows;
 
        private ArrayList _tokensForRegistration;
        private ArrayList _tokensForUnRegistration; 
 
        /// 
        /// This are the the permissions the Context has to access Visual APIs. 
        /// 
        private int _readOnlyAccessCounter;

        // the cached realization context 
        private RealizationContext _cachedRealizationContext;
 
        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 HwndTarget that is handling channel notifications, if any 
        //

        private HwndTarget _notificationHandler;
 
        // 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; 

        private ArrayList _transformHints = new ArrayList(); 
 
        // List of pending loaded event dispatcher operations
        private FrugalObjectList _loadedOrUnloadedPendingOperations; 

        // Time to wait for unthrottled renders
        private TimeSpan _timeDelay = TimeSpan.FromMilliseconds(10);
 
        // Describes if we currently do or have ever had targets containing any bitmap effects
        private bool _bitmapEffectsUsed; 
 
        // 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 bool _primaryChannelRemote;

        private List _currentRenderingChannel = new List();
 
        #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