Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / System / Windows / InterOp / D3DImage.cs / 3 / D3DImage.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: D3DImage class // An ImageSource that displays a user created D3D surface // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.KnownBoxes; using MS.Internal.PresentationCore; using MS.Win32.PresentationCore; using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using System.Windows.Media.Composition; using System.Windows.Media.Imaging; using System.Windows.Threading; using System.Security.Permissions; using System.Security; using System.Threading; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; namespace System.Windows.Interop { ////// Specifies the acceptible resources for SetBackBuffer. /// public enum D3DResourceType { IDirect3DSurface9 } ////// It's possible for a D3DImage to be created where unmanaged code permission is /// granted and then handed off to a partial trust add-in. We must protect the /// necessary virtual overrides so base class calls don't bypass the link demand. /// [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)] public class D3DImage : ImageSource, IAppDomainShutdownListener { static D3DImage() { IsFrontBufferAvailablePropertyKey = DependencyProperty.RegisterReadOnly( "IsFrontBufferAvailable", typeof(bool), typeof(D3DImage), new UIPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(IsFrontBufferAvailablePropertyChanged) ) ); IsFrontBufferAvailableProperty = IsFrontBufferAvailablePropertyKey.DependencyProperty; } ////// Default constructor, sets DPI to 96.0 /// ////// The non-default ctor demands so if we stop calling it we need to demand here /// public D3DImage() : this(96.0, 96.0) { } ////// DPI constructor /// ////// Critical - access critical types (FrontBufferAvailableCallback) /// PublicOK - class demands unmanaged code permission /// [SecurityCritical] public D3DImage(double dpiX, double dpiY) { SecurityHelper.DemandUnmanagedCode(); if (dpiX < 0) { throw new ArgumentOutOfRangeException("dpiX", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } if (dpiY < 0) { throw new ArgumentOutOfRangeException("dpiY", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } _canWriteEvent = new ManualResetEvent(true); _availableCallback = Callback; _sendPresentDelegate = SendPresent; _dpiX = dpiX; _dpiY = dpiY; _listener = new WeakReference(this); AppDomainShutdownMonitor.Add(_listener); } [SecurityCritical, SecurityTreatAsSafe] ~D3DImage() { if (_pInteropDeviceBitmap != null) { // Stop unmanaged code from sending us messages because we're being collected UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); } AppDomainShutdownMonitor.Remove(_listener); } ////// Sets a back buffer source for this D3DImage. /// /// You can only call this while locked. You only need to call this once, unless /// IsFrontBufferAvailable goes from false -> true and then you MUST call this /// again. When IsFrontBufferAvailable goes to false, we release our reference /// to your back buffer. /// /// Requirements on backBuffer by type: /// IDirect3DSurface9 /// D3DFMT_A8R8G8B8 or D3DFMT_X8R8G8B8 /// D3DUSAGE_RENDERTARGET /// D3DPOOL_DEFAULT /// Multisampling is allowed on 9Ex only /// Lockability is optional but has performance impact (see below) /// /// For best performance by type: /// IDirect3DSurface9 /// Vista WDDM: non-lockable, created on IDirect3DDevice9Ex with /// D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES and /// D3DCAPS2_CANSHARERESOURCE support. /// Vista XDDM: Doesn't matter. Software copying is fastest. /// non-Vista: Lockable with GetDC support for the pixel format and /// D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES support. /// /// ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] public void SetBackBuffer(D3DResourceType backBufferType, IntPtr backBuffer) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } // In case the user passed in something like "(D3DResourceType)-1" if (backBufferType != D3DResourceType.IDirect3DSurface9) { throw new ArgumentOutOfRangeException("backBufferType"); } // Early-out if the current back buffer equals the new one. If the front buffer // is not available, _pUserSurfaceUnsafe will be null and this check will // fail. We don't want a null backBuffer to early-out when the front buffer // isn't available. if (backBuffer != IntPtr.Zero && backBuffer == _pUserSurfaceUnsafe) { return; } SafeMILHandle newBitmap = null; uint newPixelWidth = 0; uint newPixelHeight = 0; // Create a new CInteropDeviceBitmap. Note that a null backBuffer will result // in a null _pInteropDeviceBitmap at the end if (backBuffer != IntPtr.Zero) { Guid deviceId = MediaSystem.DeviceId; HRESULT.Check(UnsafeNativeMethods.InteropDeviceBitmap.Create( /* in */ ref deviceId, backBuffer, _dpiX, _dpiY, ++_version, _availableCallback, out newBitmap, out newPixelWidth, out newPixelHeight )); } // // We need to completely disassociate with the old interop bitmap if it // exists because it won't be deleted until the composition thread is done // with it or until the garbage collector runs. // if (_pInteropDeviceBitmap != null) { // 1. Tell the old bitmap to stop sending front buffer messages because // our new back buffer may be on a different adapter. Plus, tell the // bitmap to release the back buffer in case the user wants to delete // it immediately. UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); // 2. If we were waiting for a present, unhook from commit UnsubscribeFromCommittingBatch(); // 3. We are no longer dirty _isDirty = false; // Note: We don't need to do anything to the event because we're under // the protection of Lock } // If anything about the new surface were unacceptible, we would have recieved // a bad HRESULT from Create() so everything must be good _pInteropDeviceBitmap = newBitmap; _pUserSurfaceUnsafe = backBuffer; _pixelWidth = newPixelWidth; _pixelHeight = newPixelHeight; // AddDirtyRect is usually what triggers Changed, but AddDirtyRect isn't allowed with // no back buffer so we mark for Changed here if (_pInteropDeviceBitmap == null) { _isChangePending = true; } RegisterForAsyncUpdateResource(); // WritePostscript will happen at Unlock } ////// Locks the D3DImage /// /// While locked you can call AddDirtyRect, SetBackBuffer, and Unlock. You can /// also write to your back buffer. You should not write to the back buffer without /// being locked. /// public void Lock() { WritePreamble(); LockImpl(Duration.Forever); } ////// Trys to lock the D3DImage but gives up once duration expires. Returns true /// if the lock was obtained. See Lock for more details. /// public bool TryLock(Duration timeout) { WritePreamble(); if (timeout == Duration.Automatic) { throw new ArgumentOutOfRangeException("timeout"); } return LockImpl(timeout); } ////// Unlocks the D3DImage /// /// Can only be called while locked. /// /// If you have dirtied the image with AddDirtyRect, Unlocking will trigger us to /// copy the dirty regions from the back buffer to the front buffer. While this is /// taking place, Lock will block. To avoid locking indefinitely, use TryLock. /// public void Unlock() { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } --_lockCount; if (_isDirty && _lockCount == 0) { SubscribeToCommittingBatch(); } if (_isChangePending) { _isChangePending = false; WritePostscript(); } } ////// Adds a dirty rect to the D3DImage /// /// When you update a part of your back buffer you must dirty the same area on /// the D3DImage. After you unlock, we will copy the dirty areas to the front buffer. /// /// Can only be called while locked. /// /// IMPORTANT: After five dirty rects, we will union them all together. This /// means you must have valid data outside of the dirty regions. /// ////// Critical - access critical code /// PublicOK - only deals with managed types, unmanaged call is considered safe /// [SecurityCritical] public void AddDirtyRect(Int32Rect dirtyRect) { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } if (_pInteropDeviceBitmap == null) { throw new InvalidOperationException(SR.Get(SRID.D3DImage_MustHaveBackBuffer)); } if (dirtyRect.X < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Y < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Width < 0 || dirtyRect.Width > _pixelWidth) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (dirtyRect.Height < 0 || dirtyRect.Height > _pixelHeight) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } // Unmanaged code will make sure that the rect is well-formed HRESULT.Check(UnsafeNativeMethods.InteropDeviceBitmap.AddDirtyRect( dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height, _pInteropDeviceBitmap )); // We're now dirty, but we won't consider it a change until Unlock _isDirty = true; _isChangePending = true; } ////// When true, a front buffer exists and back buffer updates will be copied forward. /// When false, a front buffer does not exist. Your changes will not be seen. /// public bool IsFrontBufferAvailable { get { return (bool)GetValue(IsFrontBufferAvailableProperty); } } public static readonly DependencyProperty IsFrontBufferAvailableProperty; ////// Event that fires when IsFrontBufferAvailable changes /// /// After a true -> false transition, you should stop updating your surface as /// we have stopped processing updates. /// /// After a false -> true transition, you MUST set a valid back buffer /// public event DependencyPropertyChangedEventHandler IsFrontBufferAvailableChanged { add { WritePreamble(); if (value != null) { _isFrontBufferAvailableChangedHandlers += value; } } remove { WritePreamble(); if (value != null) { _isFrontBufferAvailableChangedHandlers -= value; } } } ////// Width in pixels /// public int PixelWidth { get { ReadPreamble(); return (int)_pixelWidth; } } ////// Height in pixels /// public int PixelHeight { get { ReadPreamble(); return (int)_pixelHeight; } } ////// Get the width of the image in measure units (96ths of an inch). /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override double Width { get { ReadPreamble(); return ImageSource.PixelsToDIPs(_dpiX, (int)_pixelWidth); } } ////// Get the height of the image in measure units (96ths of an inch). /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override double Height { get { ReadPreamble(); return ImageSource.PixelsToDIPs(_dpiY, (int)_pixelHeight); } } ////// Get the metadata associated with this image source /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override ImageMetadata Metadata { get { ReadPreamble(); return null; } } ////// Shadows inherited Clone() with a strongly typed /// version for convenience. /// public new D3DImage Clone() { return (D3DImage)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed /// version for convenience. /// public new D3DImage CloneCurrentValue() { return (D3DImage)base.CloneCurrentValue(); } ////// Implementation of ///Freezable.CreateInstanceCore . ///The new Freezable. ////// Calls the ctor which demands unmanaged code /// protected override Freezable CreateInstanceCore() { return new D3DImage(); } ////// Freezing is not allowed because the user will always have to make /// changes to the object based upon IsFrontBufferAvailable. We could consider /// SetBackBuffer to not count as a "change" but any thread could call it /// and that would violate our synchronization assumptions. /// /// Sealed to prevent subclasses from modifying the correct behavior. /// protected sealed override bool FreezeCore(bool isChecking) { return false; } ////// Clone overrides /// /// Not sealed to allow subclasses to clone their private data /// ////// The clone overrides call CloneCommon which calls SetBackBuffer /// which demands unmanaged code /// protected override void CloneCore(Freezable sourceFreezable) { base.CloneCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void CloneCurrentValueCore(Freezable sourceFreezable) { base.CloneCurrentValueCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void GetAsFrozenCore(Freezable sourceFreezable) { base.GetAsFrozenCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { base.GetCurrentValueAsFrozenCore(sourceFreezable); CloneCommon(sourceFreezable); } ////// Gets a software copy of D3DImage. Called by printing, RTB, and BMEs. The /// user can override this to return something else in the case of the /// device lost, for example. /// ////// Critical: accesses critical code (GetAsSoftwareBitmap) /// TreatAsSafe: exposes nothing sensitive, demands unmanaged code /// [SecurityCritical, SecurityTreatAsSafe] protected internal virtual BitmapSource CopyBackBuffer() { SecurityHelper.DemandUnmanagedCode(); BitmapSource copy = null; if (_pInteropDeviceBitmap != null) { BitmapSourceSafeMILHandle pIMILBitmapSource; if (HRESULT.Succeeded(UnsafeNativeMethods.InteropDeviceBitmap.GetAsSoftwareBitmap( _pInteropDeviceBitmap, out pIMILBitmapSource ))) { // CachedBitmap will AddRef the bitmap copy = new CachedBitmap(pIMILBitmapSource); } } return copy; } ////// Calls SetBackBuffer which demands unmanaged code /// private void CloneCommon(Freezable sourceFreezable) { D3DImage source = (D3DImage)sourceFreezable; _dpiX = source._dpiX; _dpiY = source._dpiY; Lock(); // If we've lost the front buffer, _pUserSurface unsafe will be null SetBackBuffer(D3DResourceType.IDirect3DSurface9, source._pUserSurfaceUnsafe); Unlock(); } private void SubscribeToCommittingBatch() { if (!_isWaitingForPresent) { // Suppose this D3DImage is not on the main UI thread. This thread will // never commit a batch so we don't want to add an event handler to it // since it will never get removed MediaContext mediaContext = MediaContext.From(Dispatcher); if (_duceResource.IsOnChannel(mediaContext.Channel)) { mediaContext.CommittingBatch += _sendPresentDelegate; _isWaitingForPresent = true; } } } private void UnsubscribeFromCommittingBatch() { if (_isWaitingForPresent) { MediaContext mediaContext = MediaContext.From(Dispatcher); mediaContext.CommittingBatch -= _sendPresentDelegate; _isWaitingForPresent = false; } } ////// Lock implementation shared by Lock and TryLock /// private bool LockImpl(Duration timeout) { Debug.Assert(timeout != Duration.Automatic); bool lockObtained = false; if (_lockCount == UInt32.MaxValue) { throw new InvalidOperationException(SR.Get(SRID.Image_LockCountLimit)); } if (_lockCount == 0) { if (timeout == Duration.Forever) { lockObtained = _canWriteEvent.WaitOne(); } else { lockObtained = _canWriteEvent.WaitOne(timeout.TimeSpan, false); } // Consider the situation: Lock(); AddDirtyRect(); Unlock(); Lock(); return; // The Unlock will have set us up to send a present packet but since // the user re-locked the buffer we shouldn't copy forward UnsubscribeFromCommittingBatch(); } ++_lockCount; // no WritePostscript because this isn't a "change" yet return lockObtained; } private static readonly DependencyPropertyKey IsFrontBufferAvailablePropertyKey; ////// Propery changed handler for our read only IsFrontBufferAvailable DP. Calls any /// user added handlers. /// private static void IsFrontBufferAvailablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Debug.Assert(e.OldValue != e.NewValue); bool isFrontBufferAvailable = (bool)e.NewValue; D3DImage img = (D3DImage)d; if (!isFrontBufferAvailable) { // // This isn't a true "ref" to their surface, so we don't need to do this, // but if the user clones this D3DImage afterwards we don't want to // have a pontentially garbage pointer being copied // // We are not Detach()-ing from or releasing _pInteropDeviceBitmap because // if we did, we would never get a notification when the front buffer became // available again. // img._pUserSurfaceUnsafe = IntPtr.Zero; } if (img._isFrontBufferAvailableChangedHandlers != null) { img._isFrontBufferAvailableChangedHandlers(img, e); } } ////// Sends Present packet to MIL. When MIL receives the packet, it will copy the /// dirty regions to the front buffer and set the event. /// ////// Critical: This code calls critical SendCommandD3DImagePresent /// TreatAsSafe: This code does not return any critical data. It is ok to expose /// Channels are safe to call into and do not go cross domain and cross process /// [SecurityCritical, SecurityTreatAsSafe] private void SendPresent(object sender, EventArgs args) { Debug.Assert(_isDirty); Debug.Assert(_isWaitingForPresent); Debug.Assert(_lockCount == 0); UnsubscribeFromCommittingBatch(); unsafe { DUCE.MILCMD_D3DIMAGE_PRESENT data; DUCE.Channel channel = sender as DUCE.Channel; Debug.Assert(_duceResource.IsOnChannel(channel)); data.Type = MILCMD.MilCmdD3DImagePresent; data.Handle = _duceResource.GetHandle(channel); // We need to make sure the event stays alive in case we get collected before // the composition thread processes the packet IntPtr hDuplicate; IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess(); if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle( hCurrentProc, _canWriteEvent.SafeWaitHandle, hCurrentProc, out hDuplicate, 0, false, MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS )) { throw new Win32Exception(); } data.hEvent = (ulong)hDuplicate.ToPointer(); // Send packed command structure channel.SendSecurityCriticalCommand( (byte*)&data, sizeof(DUCE.MILCMD_D3DIMAGE_PRESENT) ); } _isDirty = false; // Block on next Lock _canWriteEvent.Reset(); } ////// The DispatcherOperation corresponding to the composition thread callback. /// /// If the back buffer version matches the current version, update the front /// buffer status. /// private object SetIsFrontBufferAvailable(object isAvailableVersionPair) { Pair pair = (Pair)isAvailableVersionPair; uint version = (uint)pair.Second; if (version == _version) { bool isFrontBufferAvailable = (bool)pair.First; SetValue(IsFrontBufferAvailablePropertyKey, isFrontBufferAvailable); } // ...just because DispatcherOperationCallback requires returning an object return null; } ////// *** WARNING ***: This runs on the composition thread! /// /// What the composition thread calls when it wants to update D3DImage about /// front buffer state. It may be called multiple times with the same value. /// Since we're on a different thread we can't touch this, so queue a dispatcher /// operation. /// // NOTE: Called from the render thread!We must execute the reaction on the UI thread. private void Callback(bool isFrontBufferAvailable, uint version) { Dispatcher.BeginInvoke( DispatcherPriority.Normal, new DispatcherOperationCallback(SetIsFrontBufferAvailable), new Pair(BooleanBoxes.Box(isFrontBufferAvailable), version) ); } ////// Critical: This code calls into an unsafe code block /// TreatAsSafe: This code does not return any critical data. It is ok to expose /// Channels are safe to call into and do not go cross domain and cross process /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck) { // If we're told we can skip the channel check, then we must be on channel Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); if (skipOnChannelCheck || _duceResource.IsOnChannel(channel)) { base.UpdateResource(channel, skipOnChannelCheck); DUCE.MILCMD_D3DIMAGE data; unsafe { data.Type = MILCMD.MilCmdD3DImage; data.Handle = _duceResource.GetHandle(channel); if (_pInteropDeviceBitmap != null) { UnsafeNativeMethods.MILUnknown.AddRef(_pInteropDeviceBitmap); data.pInteropDeviceBitmap = (ulong)_pInteropDeviceBitmap.DangerousGetHandle().ToPointer(); } else { data.pInteropDeviceBitmap = 0; } data.pSoftwareBitmap = 0; if (channel.IsSynchronous) { _softwareCopy = CopyBackBuffer(); if (_softwareCopy != null) { UnsafeNativeMethods.MILUnknown.AddRef(_softwareCopy.WicSourceHandle); data.pSoftwareBitmap = (ulong)_softwareCopy.WicSourceHandle.DangerousGetHandle().ToPointer(); } } // Send packed command structure channel.SendSecurityCriticalCommand( (byte*)&data, sizeof(DUCE.MILCMD_D3DIMAGE) ); } } } internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_D3DIMAGE)) { AddRefOnChannelAnimations(channel); UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ ); // If we are being put onto the asynchronous compositor channel in // a dirty state, we need to subscribe to the commit batch event. if (!channel.IsSynchronous && _isDirty) { SubscribeToCommittingBatch(); } } return _duceResource.GetHandle(channel); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { ReleaseOnChannelAnimations(channel); // If we are being pulled off the asynchronous compositor channel // while we still have a handler hooked to the commit batch event, // remove the handler to avoid situations where would leak the D3DImage if (!channel.IsSynchronous) { UnsubscribeFromCommittingBatch(); } } } internal override DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel) { // Note that we are in a lock here already. return _duceResource.GetHandle(channel); } internal override int GetChannelCountCore() { // must already be in composition lock here return _duceResource.GetChannelCount(); } internal override DUCE.Channel GetChannelCore(int index) { // Note that we are in a lock here already. return _duceResource.GetChannel(index); } // IAppDomainShutdownListener [SecurityCritical, SecurityTreatAsSafe] void IAppDomainShutdownListener.NotifyShutdown() { if (_pInteropDeviceBitmap != null) { // Stop unmanaged code from sending us messages because the AppDomain is going down UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); } // The finalizer does the same thing, so it's no longer necessary GC.SuppressFinalize(this); } internal System.Windows.Media.Composition.DUCE.MultiChannelResource _duceResource = new System.Windows.Media.Composition.DUCE.MultiChannelResource(); // User-specified DPI of their surface private double _dpiX; private double _dpiY; // Handle to our unmanaged interop bitmap that is the front buffer. This can be null! private SafeMILHandle _pInteropDeviceBitmap; // This will be null until the user prints, uses an RTB, or uses a BME. It's the result // of calling CopyBackBuffer() which could be something created by the user. We must // hold onto it indefinitely because we're never sure when the render thread is done // with it (not even ReleaseOnChannel because it queues a command) private BitmapSource _softwareCopy; // Pointer to the user's surface that IS NOT ADDREF'D (hence the unsafe). Used // only for cloning purposes. This can be IntPtr.Zero! private IntPtr _pUserSurfaceUnsafe; // Event used to synchronize between the UI and composition thread. This prevents the user // from writing to the back buffer while MIL is copying to the front buffer. private ManualResetEvent _canWriteEvent; // Per-instance callback from composition thread plus handlers to notify private UnsafeNativeMethods.InteropDeviceBitmap.FrontBufferAvailableCallback _availableCallback; private DependencyPropertyChangedEventHandler _isFrontBufferAvailableChangedHandlers; // We'll be adding and removing a lot from CommittingBatch, so create the delegate up front // to avoid creating many during runtime. private EventHandler _sendPresentDelegate; // WeakReference to this used to listen to AddDomain shutdown private WeakReference _listener; // Keeps track of how many times the user has nested Lock private uint _lockCount; // Size of the user's surface in pixels private uint _pixelWidth; private uint _pixelHeight; // // This is incremented every time a new back buffer is set and it's passed to the bitmap. // When we recieve a front buffer notification, we ignore it if it doesn't match the current // version. // // Of course SetBackBuffer calls detach to tell the old bitmap to stop sending messages, but // it is possible the old buffer queued a message right before detach happened. If the old // back buffer and new back buffer are on the same adapter, it's not really an issue. If they // are on different ones, it's a big problem because we don't want to give incorrect front // buffer updates. Since we have to at least send the adapter back, let's instead use a // version counter that will guarantee we receive no updates from the old bitmap. // // Now if the user happens to call SetBackBuffer 2^32 times exactly we'll end up back // and the same version and could get a bad message, but that won't break anything. // private uint _version; // True if we're dirty and need to send a present private bool _isDirty; // Used to prevent Unlock from registering for commit notification more than once private bool _isWaitingForPresent; // Set to true when we want to fire Changed in Unlock private bool _isChangePending; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: D3DImage class // An ImageSource that displays a user created D3D surface // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.KnownBoxes; using MS.Internal.PresentationCore; using MS.Win32.PresentationCore; using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using System.Windows.Media.Composition; using System.Windows.Media.Imaging; using System.Windows.Threading; using System.Security.Permissions; using System.Security; using System.Threading; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; namespace System.Windows.Interop { ////// Specifies the acceptible resources for SetBackBuffer. /// public enum D3DResourceType { IDirect3DSurface9 } ////// It's possible for a D3DImage to be created where unmanaged code permission is /// granted and then handed off to a partial trust add-in. We must protect the /// necessary virtual overrides so base class calls don't bypass the link demand. /// [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)] public class D3DImage : ImageSource, IAppDomainShutdownListener { static D3DImage() { IsFrontBufferAvailablePropertyKey = DependencyProperty.RegisterReadOnly( "IsFrontBufferAvailable", typeof(bool), typeof(D3DImage), new UIPropertyMetadata( BooleanBoxes.TrueBox, new PropertyChangedCallback(IsFrontBufferAvailablePropertyChanged) ) ); IsFrontBufferAvailableProperty = IsFrontBufferAvailablePropertyKey.DependencyProperty; } ////// Default constructor, sets DPI to 96.0 /// ////// The non-default ctor demands so if we stop calling it we need to demand here /// public D3DImage() : this(96.0, 96.0) { } ////// DPI constructor /// ////// Critical - access critical types (FrontBufferAvailableCallback) /// PublicOK - class demands unmanaged code permission /// [SecurityCritical] public D3DImage(double dpiX, double dpiY) { SecurityHelper.DemandUnmanagedCode(); if (dpiX < 0) { throw new ArgumentOutOfRangeException("dpiX", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } if (dpiY < 0) { throw new ArgumentOutOfRangeException("dpiY", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } _canWriteEvent = new ManualResetEvent(true); _availableCallback = Callback; _sendPresentDelegate = SendPresent; _dpiX = dpiX; _dpiY = dpiY; _listener = new WeakReference(this); AppDomainShutdownMonitor.Add(_listener); } [SecurityCritical, SecurityTreatAsSafe] ~D3DImage() { if (_pInteropDeviceBitmap != null) { // Stop unmanaged code from sending us messages because we're being collected UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); } AppDomainShutdownMonitor.Remove(_listener); } ////// Sets a back buffer source for this D3DImage. /// /// You can only call this while locked. You only need to call this once, unless /// IsFrontBufferAvailable goes from false -> true and then you MUST call this /// again. When IsFrontBufferAvailable goes to false, we release our reference /// to your back buffer. /// /// Requirements on backBuffer by type: /// IDirect3DSurface9 /// D3DFMT_A8R8G8B8 or D3DFMT_X8R8G8B8 /// D3DUSAGE_RENDERTARGET /// D3DPOOL_DEFAULT /// Multisampling is allowed on 9Ex only /// Lockability is optional but has performance impact (see below) /// /// For best performance by type: /// IDirect3DSurface9 /// Vista WDDM: non-lockable, created on IDirect3DDevice9Ex with /// D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES and /// D3DCAPS2_CANSHARERESOURCE support. /// Vista XDDM: Doesn't matter. Software copying is fastest. /// non-Vista: Lockable with GetDC support for the pixel format and /// D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES support. /// /// ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] public void SetBackBuffer(D3DResourceType backBufferType, IntPtr backBuffer) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } // In case the user passed in something like "(D3DResourceType)-1" if (backBufferType != D3DResourceType.IDirect3DSurface9) { throw new ArgumentOutOfRangeException("backBufferType"); } // Early-out if the current back buffer equals the new one. If the front buffer // is not available, _pUserSurfaceUnsafe will be null and this check will // fail. We don't want a null backBuffer to early-out when the front buffer // isn't available. if (backBuffer != IntPtr.Zero && backBuffer == _pUserSurfaceUnsafe) { return; } SafeMILHandle newBitmap = null; uint newPixelWidth = 0; uint newPixelHeight = 0; // Create a new CInteropDeviceBitmap. Note that a null backBuffer will result // in a null _pInteropDeviceBitmap at the end if (backBuffer != IntPtr.Zero) { Guid deviceId = MediaSystem.DeviceId; HRESULT.Check(UnsafeNativeMethods.InteropDeviceBitmap.Create( /* in */ ref deviceId, backBuffer, _dpiX, _dpiY, ++_version, _availableCallback, out newBitmap, out newPixelWidth, out newPixelHeight )); } // // We need to completely disassociate with the old interop bitmap if it // exists because it won't be deleted until the composition thread is done // with it or until the garbage collector runs. // if (_pInteropDeviceBitmap != null) { // 1. Tell the old bitmap to stop sending front buffer messages because // our new back buffer may be on a different adapter. Plus, tell the // bitmap to release the back buffer in case the user wants to delete // it immediately. UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); // 2. If we were waiting for a present, unhook from commit UnsubscribeFromCommittingBatch(); // 3. We are no longer dirty _isDirty = false; // Note: We don't need to do anything to the event because we're under // the protection of Lock } // If anything about the new surface were unacceptible, we would have recieved // a bad HRESULT from Create() so everything must be good _pInteropDeviceBitmap = newBitmap; _pUserSurfaceUnsafe = backBuffer; _pixelWidth = newPixelWidth; _pixelHeight = newPixelHeight; // AddDirtyRect is usually what triggers Changed, but AddDirtyRect isn't allowed with // no back buffer so we mark for Changed here if (_pInteropDeviceBitmap == null) { _isChangePending = true; } RegisterForAsyncUpdateResource(); // WritePostscript will happen at Unlock } ////// Locks the D3DImage /// /// While locked you can call AddDirtyRect, SetBackBuffer, and Unlock. You can /// also write to your back buffer. You should not write to the back buffer without /// being locked. /// public void Lock() { WritePreamble(); LockImpl(Duration.Forever); } ////// Trys to lock the D3DImage but gives up once duration expires. Returns true /// if the lock was obtained. See Lock for more details. /// public bool TryLock(Duration timeout) { WritePreamble(); if (timeout == Duration.Automatic) { throw new ArgumentOutOfRangeException("timeout"); } return LockImpl(timeout); } ////// Unlocks the D3DImage /// /// Can only be called while locked. /// /// If you have dirtied the image with AddDirtyRect, Unlocking will trigger us to /// copy the dirty regions from the back buffer to the front buffer. While this is /// taking place, Lock will block. To avoid locking indefinitely, use TryLock. /// public void Unlock() { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } --_lockCount; if (_isDirty && _lockCount == 0) { SubscribeToCommittingBatch(); } if (_isChangePending) { _isChangePending = false; WritePostscript(); } } ////// Adds a dirty rect to the D3DImage /// /// When you update a part of your back buffer you must dirty the same area on /// the D3DImage. After you unlock, we will copy the dirty areas to the front buffer. /// /// Can only be called while locked. /// /// IMPORTANT: After five dirty rects, we will union them all together. This /// means you must have valid data outside of the dirty regions. /// ////// Critical - access critical code /// PublicOK - only deals with managed types, unmanaged call is considered safe /// [SecurityCritical] public void AddDirtyRect(Int32Rect dirtyRect) { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } if (_pInteropDeviceBitmap == null) { throw new InvalidOperationException(SR.Get(SRID.D3DImage_MustHaveBackBuffer)); } if (dirtyRect.X < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Y < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Width < 0 || dirtyRect.Width > _pixelWidth) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (dirtyRect.Height < 0 || dirtyRect.Height > _pixelHeight) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } // Unmanaged code will make sure that the rect is well-formed HRESULT.Check(UnsafeNativeMethods.InteropDeviceBitmap.AddDirtyRect( dirtyRect.X, dirtyRect.Y, dirtyRect.Width, dirtyRect.Height, _pInteropDeviceBitmap )); // We're now dirty, but we won't consider it a change until Unlock _isDirty = true; _isChangePending = true; } ////// When true, a front buffer exists and back buffer updates will be copied forward. /// When false, a front buffer does not exist. Your changes will not be seen. /// public bool IsFrontBufferAvailable { get { return (bool)GetValue(IsFrontBufferAvailableProperty); } } public static readonly DependencyProperty IsFrontBufferAvailableProperty; ////// Event that fires when IsFrontBufferAvailable changes /// /// After a true -> false transition, you should stop updating your surface as /// we have stopped processing updates. /// /// After a false -> true transition, you MUST set a valid back buffer /// public event DependencyPropertyChangedEventHandler IsFrontBufferAvailableChanged { add { WritePreamble(); if (value != null) { _isFrontBufferAvailableChangedHandlers += value; } } remove { WritePreamble(); if (value != null) { _isFrontBufferAvailableChangedHandlers -= value; } } } ////// Width in pixels /// public int PixelWidth { get { ReadPreamble(); return (int)_pixelWidth; } } ////// Height in pixels /// public int PixelHeight { get { ReadPreamble(); return (int)_pixelHeight; } } ////// Get the width of the image in measure units (96ths of an inch). /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override double Width { get { ReadPreamble(); return ImageSource.PixelsToDIPs(_dpiX, (int)_pixelWidth); } } ////// Get the height of the image in measure units (96ths of an inch). /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override double Height { get { ReadPreamble(); return ImageSource.PixelsToDIPs(_dpiY, (int)_pixelHeight); } } ////// Get the metadata associated with this image source /// /// Sealed to prevent subclasses from modifying the correct behavior. /// public sealed override ImageMetadata Metadata { get { ReadPreamble(); return null; } } ////// Shadows inherited Clone() with a strongly typed /// version for convenience. /// public new D3DImage Clone() { return (D3DImage)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed /// version for convenience. /// public new D3DImage CloneCurrentValue() { return (D3DImage)base.CloneCurrentValue(); } ////// Implementation of ///Freezable.CreateInstanceCore . ///The new Freezable. ////// Calls the ctor which demands unmanaged code /// protected override Freezable CreateInstanceCore() { return new D3DImage(); } ////// Freezing is not allowed because the user will always have to make /// changes to the object based upon IsFrontBufferAvailable. We could consider /// SetBackBuffer to not count as a "change" but any thread could call it /// and that would violate our synchronization assumptions. /// /// Sealed to prevent subclasses from modifying the correct behavior. /// protected sealed override bool FreezeCore(bool isChecking) { return false; } ////// Clone overrides /// /// Not sealed to allow subclasses to clone their private data /// ////// The clone overrides call CloneCommon which calls SetBackBuffer /// which demands unmanaged code /// protected override void CloneCore(Freezable sourceFreezable) { base.CloneCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void CloneCurrentValueCore(Freezable sourceFreezable) { base.CloneCurrentValueCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void GetAsFrozenCore(Freezable sourceFreezable) { base.GetAsFrozenCore(sourceFreezable); CloneCommon(sourceFreezable); } protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { base.GetCurrentValueAsFrozenCore(sourceFreezable); CloneCommon(sourceFreezable); } ////// Gets a software copy of D3DImage. Called by printing, RTB, and BMEs. The /// user can override this to return something else in the case of the /// device lost, for example. /// ////// Critical: accesses critical code (GetAsSoftwareBitmap) /// TreatAsSafe: exposes nothing sensitive, demands unmanaged code /// [SecurityCritical, SecurityTreatAsSafe] protected internal virtual BitmapSource CopyBackBuffer() { SecurityHelper.DemandUnmanagedCode(); BitmapSource copy = null; if (_pInteropDeviceBitmap != null) { BitmapSourceSafeMILHandle pIMILBitmapSource; if (HRESULT.Succeeded(UnsafeNativeMethods.InteropDeviceBitmap.GetAsSoftwareBitmap( _pInteropDeviceBitmap, out pIMILBitmapSource ))) { // CachedBitmap will AddRef the bitmap copy = new CachedBitmap(pIMILBitmapSource); } } return copy; } ////// Calls SetBackBuffer which demands unmanaged code /// private void CloneCommon(Freezable sourceFreezable) { D3DImage source = (D3DImage)sourceFreezable; _dpiX = source._dpiX; _dpiY = source._dpiY; Lock(); // If we've lost the front buffer, _pUserSurface unsafe will be null SetBackBuffer(D3DResourceType.IDirect3DSurface9, source._pUserSurfaceUnsafe); Unlock(); } private void SubscribeToCommittingBatch() { if (!_isWaitingForPresent) { // Suppose this D3DImage is not on the main UI thread. This thread will // never commit a batch so we don't want to add an event handler to it // since it will never get removed MediaContext mediaContext = MediaContext.From(Dispatcher); if (_duceResource.IsOnChannel(mediaContext.Channel)) { mediaContext.CommittingBatch += _sendPresentDelegate; _isWaitingForPresent = true; } } } private void UnsubscribeFromCommittingBatch() { if (_isWaitingForPresent) { MediaContext mediaContext = MediaContext.From(Dispatcher); mediaContext.CommittingBatch -= _sendPresentDelegate; _isWaitingForPresent = false; } } ////// Lock implementation shared by Lock and TryLock /// private bool LockImpl(Duration timeout) { Debug.Assert(timeout != Duration.Automatic); bool lockObtained = false; if (_lockCount == UInt32.MaxValue) { throw new InvalidOperationException(SR.Get(SRID.Image_LockCountLimit)); } if (_lockCount == 0) { if (timeout == Duration.Forever) { lockObtained = _canWriteEvent.WaitOne(); } else { lockObtained = _canWriteEvent.WaitOne(timeout.TimeSpan, false); } // Consider the situation: Lock(); AddDirtyRect(); Unlock(); Lock(); return; // The Unlock will have set us up to send a present packet but since // the user re-locked the buffer we shouldn't copy forward UnsubscribeFromCommittingBatch(); } ++_lockCount; // no WritePostscript because this isn't a "change" yet return lockObtained; } private static readonly DependencyPropertyKey IsFrontBufferAvailablePropertyKey; ////// Propery changed handler for our read only IsFrontBufferAvailable DP. Calls any /// user added handlers. /// private static void IsFrontBufferAvailablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { Debug.Assert(e.OldValue != e.NewValue); bool isFrontBufferAvailable = (bool)e.NewValue; D3DImage img = (D3DImage)d; if (!isFrontBufferAvailable) { // // This isn't a true "ref" to their surface, so we don't need to do this, // but if the user clones this D3DImage afterwards we don't want to // have a pontentially garbage pointer being copied // // We are not Detach()-ing from or releasing _pInteropDeviceBitmap because // if we did, we would never get a notification when the front buffer became // available again. // img._pUserSurfaceUnsafe = IntPtr.Zero; } if (img._isFrontBufferAvailableChangedHandlers != null) { img._isFrontBufferAvailableChangedHandlers(img, e); } } ////// Sends Present packet to MIL. When MIL receives the packet, it will copy the /// dirty regions to the front buffer and set the event. /// ////// Critical: This code calls critical SendCommandD3DImagePresent /// TreatAsSafe: This code does not return any critical data. It is ok to expose /// Channels are safe to call into and do not go cross domain and cross process /// [SecurityCritical, SecurityTreatAsSafe] private void SendPresent(object sender, EventArgs args) { Debug.Assert(_isDirty); Debug.Assert(_isWaitingForPresent); Debug.Assert(_lockCount == 0); UnsubscribeFromCommittingBatch(); unsafe { DUCE.MILCMD_D3DIMAGE_PRESENT data; DUCE.Channel channel = sender as DUCE.Channel; Debug.Assert(_duceResource.IsOnChannel(channel)); data.Type = MILCMD.MilCmdD3DImagePresent; data.Handle = _duceResource.GetHandle(channel); // We need to make sure the event stays alive in case we get collected before // the composition thread processes the packet IntPtr hDuplicate; IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess(); if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle( hCurrentProc, _canWriteEvent.SafeWaitHandle, hCurrentProc, out hDuplicate, 0, false, MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS )) { throw new Win32Exception(); } data.hEvent = (ulong)hDuplicate.ToPointer(); // Send packed command structure channel.SendSecurityCriticalCommand( (byte*)&data, sizeof(DUCE.MILCMD_D3DIMAGE_PRESENT) ); } _isDirty = false; // Block on next Lock _canWriteEvent.Reset(); } ////// The DispatcherOperation corresponding to the composition thread callback. /// /// If the back buffer version matches the current version, update the front /// buffer status. /// private object SetIsFrontBufferAvailable(object isAvailableVersionPair) { Pair pair = (Pair)isAvailableVersionPair; uint version = (uint)pair.Second; if (version == _version) { bool isFrontBufferAvailable = (bool)pair.First; SetValue(IsFrontBufferAvailablePropertyKey, isFrontBufferAvailable); } // ...just because DispatcherOperationCallback requires returning an object return null; } ////// *** WARNING ***: This runs on the composition thread! /// /// What the composition thread calls when it wants to update D3DImage about /// front buffer state. It may be called multiple times with the same value. /// Since we're on a different thread we can't touch this, so queue a dispatcher /// operation. /// // NOTE: Called from the render thread!We must execute the reaction on the UI thread. private void Callback(bool isFrontBufferAvailable, uint version) { Dispatcher.BeginInvoke( DispatcherPriority.Normal, new DispatcherOperationCallback(SetIsFrontBufferAvailable), new Pair(BooleanBoxes.Box(isFrontBufferAvailable), version) ); } ////// Critical: This code calls into an unsafe code block /// TreatAsSafe: This code does not return any critical data. It is ok to expose /// Channels are safe to call into and do not go cross domain and cross process /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck) { // If we're told we can skip the channel check, then we must be on channel Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); if (skipOnChannelCheck || _duceResource.IsOnChannel(channel)) { base.UpdateResource(channel, skipOnChannelCheck); DUCE.MILCMD_D3DIMAGE data; unsafe { data.Type = MILCMD.MilCmdD3DImage; data.Handle = _duceResource.GetHandle(channel); if (_pInteropDeviceBitmap != null) { UnsafeNativeMethods.MILUnknown.AddRef(_pInteropDeviceBitmap); data.pInteropDeviceBitmap = (ulong)_pInteropDeviceBitmap.DangerousGetHandle().ToPointer(); } else { data.pInteropDeviceBitmap = 0; } data.pSoftwareBitmap = 0; if (channel.IsSynchronous) { _softwareCopy = CopyBackBuffer(); if (_softwareCopy != null) { UnsafeNativeMethods.MILUnknown.AddRef(_softwareCopy.WicSourceHandle); data.pSoftwareBitmap = (ulong)_softwareCopy.WicSourceHandle.DangerousGetHandle().ToPointer(); } } // Send packed command structure channel.SendSecurityCriticalCommand( (byte*)&data, sizeof(DUCE.MILCMD_D3DIMAGE) ); } } } internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(channel, System.Windows.Media.Composition.DUCE.ResourceType.TYPE_D3DIMAGE)) { AddRefOnChannelAnimations(channel); UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ ); // If we are being put onto the asynchronous compositor channel in // a dirty state, we need to subscribe to the commit batch event. if (!channel.IsSynchronous && _isDirty) { SubscribeToCommittingBatch(); } } return _duceResource.GetHandle(channel); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { ReleaseOnChannelAnimations(channel); // If we are being pulled off the asynchronous compositor channel // while we still have a handler hooked to the commit batch event, // remove the handler to avoid situations where would leak the D3DImage if (!channel.IsSynchronous) { UnsubscribeFromCommittingBatch(); } } } internal override DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel) { // Note that we are in a lock here already. return _duceResource.GetHandle(channel); } internal override int GetChannelCountCore() { // must already be in composition lock here return _duceResource.GetChannelCount(); } internal override DUCE.Channel GetChannelCore(int index) { // Note that we are in a lock here already. return _duceResource.GetChannel(index); } // IAppDomainShutdownListener [SecurityCritical, SecurityTreatAsSafe] void IAppDomainShutdownListener.NotifyShutdown() { if (_pInteropDeviceBitmap != null) { // Stop unmanaged code from sending us messages because the AppDomain is going down UnsafeNativeMethods.InteropDeviceBitmap.Detach(_pInteropDeviceBitmap); } // The finalizer does the same thing, so it's no longer necessary GC.SuppressFinalize(this); } internal System.Windows.Media.Composition.DUCE.MultiChannelResource _duceResource = new System.Windows.Media.Composition.DUCE.MultiChannelResource(); // User-specified DPI of their surface private double _dpiX; private double _dpiY; // Handle to our unmanaged interop bitmap that is the front buffer. This can be null! private SafeMILHandle _pInteropDeviceBitmap; // This will be null until the user prints, uses an RTB, or uses a BME. It's the result // of calling CopyBackBuffer() which could be something created by the user. We must // hold onto it indefinitely because we're never sure when the render thread is done // with it (not even ReleaseOnChannel because it queues a command) private BitmapSource _softwareCopy; // Pointer to the user's surface that IS NOT ADDREF'D (hence the unsafe). Used // only for cloning purposes. This can be IntPtr.Zero! private IntPtr _pUserSurfaceUnsafe; // Event used to synchronize between the UI and composition thread. This prevents the user // from writing to the back buffer while MIL is copying to the front buffer. private ManualResetEvent _canWriteEvent; // Per-instance callback from composition thread plus handlers to notify private UnsafeNativeMethods.InteropDeviceBitmap.FrontBufferAvailableCallback _availableCallback; private DependencyPropertyChangedEventHandler _isFrontBufferAvailableChangedHandlers; // We'll be adding and removing a lot from CommittingBatch, so create the delegate up front // to avoid creating many during runtime. private EventHandler _sendPresentDelegate; // WeakReference to this used to listen to AddDomain shutdown private WeakReference _listener; // Keeps track of how many times the user has nested Lock private uint _lockCount; // Size of the user's surface in pixels private uint _pixelWidth; private uint _pixelHeight; // // This is incremented every time a new back buffer is set and it's passed to the bitmap. // When we recieve a front buffer notification, we ignore it if it doesn't match the current // version. // // Of course SetBackBuffer calls detach to tell the old bitmap to stop sending messages, but // it is possible the old buffer queued a message right before detach happened. If the old // back buffer and new back buffer are on the same adapter, it's not really an issue. If they // are on different ones, it's a big problem because we don't want to give incorrect front // buffer updates. Since we have to at least send the adapter back, let's instead use a // version counter that will guarantee we receive no updates from the old bitmap. // // Now if the user happens to call SetBackBuffer 2^32 times exactly we'll end up back // and the same version and could get a bad message, but that won't break anything. // private uint _version; // True if we're dirty and need to send a present private bool _isDirty; // Used to prevent Unlock from registering for commit notification more than once private bool _isWaitingForPresent; // Set to true when we want to fire Changed in Unlock private bool _isChangePending; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataBindingsDialog.cs
- DisplayInformation.cs
- DecimalSumAggregationOperator.cs
- ThreadInterruptedException.cs
- SmiGettersStream.cs
- HandlerWithFactory.cs
- EventManager.cs
- SqlCommandBuilder.cs
- MaterialGroup.cs
- ErrorStyle.cs
- HashSetEqualityComparer.cs
- CultureInfoConverter.cs
- QuotedStringFormatReader.cs
- HttpWebResponse.cs
- XmlAnyElementAttribute.cs
- PtsHost.cs
- CellTreeNodeVisitors.cs
- MailHeaderInfo.cs
- Schema.cs
- KeyValuePairs.cs
- ZoneButton.cs
- SerializerProvider.cs
- XamlNamespaceHelper.cs
- ISessionStateStore.cs
- parserscommon.cs
- ProxyAttribute.cs
- SettingsPropertyIsReadOnlyException.cs
- DayRenderEvent.cs
- RequestCachePolicy.cs
- PageFunction.cs
- LightweightCodeGenerator.cs
- TextTreeUndo.cs
- RewritingSimplifier.cs
- unsafeIndexingFilterStream.cs
- VisualBrush.cs
- DeclaredTypeElement.cs
- XmlDataSourceView.cs
- Tag.cs
- IPCCacheManager.cs
- OutputScopeManager.cs
- ControlParameter.cs
- DesignTimeSiteMapProvider.cs
- Stack.cs
- ButtonBase.cs
- GridToolTip.cs
- Codec.cs
- ProfessionalColors.cs
- ICspAsymmetricAlgorithm.cs
- DataControlImageButton.cs
- ComponentResourceKeyConverter.cs
- DBConcurrencyException.cs
- AttachedAnnotationChangedEventArgs.cs
- XamlToRtfWriter.cs
- CodeGotoStatement.cs
- Int32.cs
- FlowDocument.cs
- AttributeUsageAttribute.cs
- shaperfactoryquerycachekey.cs
- InstanceValue.cs
- DataGridColumnsPage.cs
- ListSortDescription.cs
- ObfuscateAssemblyAttribute.cs
- BinaryObjectInfo.cs
- SimpleExpression.cs
- ZipIOExtraField.cs
- EntityTypeEmitter.cs
- HyperLinkStyle.cs
- FontInfo.cs
- TagPrefixCollection.cs
- FacetDescriptionElement.cs
- Application.cs
- EncryptionUtility.cs
- FirstMatchCodeGroup.cs
- rsa.cs
- ResourceWriter.cs
- SqlCommandSet.cs
- TreeNodeSelectionProcessor.cs
- PrinterUnitConvert.cs
- DataAdapter.cs
- SparseMemoryStream.cs
- ControlPaint.cs
- GradientPanel.cs
- LinkConverter.cs
- ListBase.cs
- ConnectionManagementSection.cs
- ContextMenuStrip.cs
- ProxyHelper.cs
- XmlLoader.cs
- CounterNameConverter.cs
- VirtualDirectoryMappingCollection.cs
- DiffuseMaterial.cs
- GroupQuery.cs
- SmtpNetworkElement.cs
- TextViewSelectionProcessor.cs
- UnmanagedHandle.cs
- EntityDataSourceView.cs
- HMACSHA512.cs
- ForeignConstraint.cs
- ConnectivityStatus.cs
- WebPartPersonalization.cs