private void SendPresent(object sender, EventArgs args)
{
Debug.Assert(_isDirty);
Debug.Assert(_isWaitingForPresent);
Debug.Assert(_lockCount == 0);
//
// If we were waiting for present when the bitmap changed, SetBackBuffer removed
// us from waiting for present. So if this is true then the NEW bitmap has been
// dirtied before it has been sent to the render thread. We need to delay the
// present until after the update resource because the D3DImage resource is still
// referencing the old bitmap.
//
if (_waitingForUpdateResourceBecauseBitmapChanged)
{
return;
}
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
// Note that the command is sent in its own batch (sendInSeparateBatch == true) because this method is called under the
// context of the MediaContext.CommitChannel and the command needs to make it into the current set of changes which are
// being commited to the compositor. If the command would not be added to a separate batch, it would go into the
// "future" batch which would not get submitted this time around. This leads to a dead-lock situation which occurs when
// the app calls Lock on the D3DImage because Lock waits on _canWriteEvent which the compositor sets when it sees the
// Present command. However, since the compositor does not get the Present command, it will not set the event and the
// UI thread will wait forever on the compositor which will hang the application.
channel.SendCommand(
(byte*)&data,
sizeof(DUCE.MILCMD_D3DIMAGE_PRESENT),
true /* sendInSeparateBatch */
);
}
_isDirty = false;
// Block on next Lock
_canWriteEvent.Reset();
}