Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Sascha-L committed Jan 19, 2017
2 parents 5b2236d + 3f44720 commit 7314b5e
Showing 1 changed file with 77 additions and 41 deletions.
118 changes: 77 additions & 41 deletions Source/DirectShow/Controls/D3DRenderer.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
Expand Down Expand Up @@ -57,6 +58,17 @@ public abstract class D3DRenderer : FrameworkElement
/// This is used to remember the value for when the control is loaded and unloaded.
/// </summary>
private bool m_renderOnCompositionTargetRenderingTemp;

/// <summary>
/// TryLock timeout for the invalidate video image. Low values means higher UI responsivity, but more video dropped frames.
/// </summary>
private Duration m_invalidateVideoImageLockDuration = new Duration(TimeSpan.FromMilliseconds(100));

/// <summary>
/// Flag to reduce redundant calls to the AddDirtyRect when the rendering thread is busy.
/// Int instead of bool for Interlocked support.
/// </summary>
private int m_videoImageInvalid = 1;
#endregion

#region Dependency Properties
Expand Down Expand Up @@ -324,44 +336,13 @@ private void CompositionTargetRendering(object sender, EventArgs e)
InternalInvalidateVideoImage();
}

/// <summary>
/// Cleans up any dead references we may have to any cloned renderers
/// </summary>
private void CleanZombieRenderers()
{
lock (m_clonedD3Drenderers)
{
var deadObjects = new List<WeakReference>();

for (int i = 0; i < m_clonedD3Drenderers.Count; i++)
{
if (!m_clonedD3Drenderers[i].IsAlive)
deadObjects.Add(m_clonedD3Drenderers[i]);
}

foreach (var deadGuy in deadObjects)
{
m_clonedD3Drenderers.Remove(deadGuy);
}
}
}

/// <summary>
/// Sets the backbuffer for any cloned D3DRenderers
/// </summary>
private void SetBackBufferForClones()
{
lock (m_clonedD3Drenderers)
{
CleanZombieRenderers();

foreach (var rendererRef in m_clonedD3Drenderers)
{
var renderer = rendererRef.Target as D3DRenderer;
if (renderer != null)
renderer.SetBackBuffer(m_pBackBuffer);
}
}
var backBuffer = m_pBackBuffer;
ForEachCloneD3DRenderer(r => r.SetBackBuffer(backBuffer));
}

/// <summary>
Expand All @@ -387,11 +368,15 @@ private void SetBackBufferInternal(IntPtr backBuffer)
{
D3DImage.Lock();
D3DImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, backBuffer);
D3DImage.Unlock();
SetNaturalWidthHeight();
}
catch
{ }
finally
{
D3DImage.Unlock();
}

SetNaturalWidthHeight();

/* Clear our flag, so this won't be ran again
* until a new surface is sent */
Expand All @@ -404,21 +389,47 @@ private void SetNaturalWidthHeight()
SetNaturalVideoWidth(m_d3dImage.PixelWidth);
}

private bool GetSetVideoImageInvalid(bool value)
{
int oldValue = Interlocked.Exchange(ref m_videoImageInvalid, value ? 1 : 0);
return oldValue == 1;
}

/// <summary>
/// Invalidates any possible cloned renderer we may have
/// </summary>
private void InvalidateClonedVideoImages()
{
ForEachCloneD3DRenderer(r => r.InvalidateVideoImage());
}

private void ForEachCloneD3DRenderer(Action<D3DRenderer> action)
{
lock (m_clonedD3Drenderers)
{
CleanZombieRenderers();

bool needClean = false;
foreach (var rendererRef in m_clonedD3Drenderers)
{
var renderer = rendererRef.Target as D3DRenderer;
if (renderer != null)
renderer.InvalidateVideoImage();
action(renderer);
else
needClean = true;
}

if (needClean)
CleanZombieRenderers();
}
}

/// <summary>
/// Cleans up any dead references we may have to any cloned renderers
/// </summary>
private void CleanZombieRenderers()
{
lock (m_clonedD3Drenderers)
{
m_clonedD3Drenderers.RemoveAll(c => !c.IsAlive);
}
}

Expand Down Expand Up @@ -533,6 +544,8 @@ protected void SetBackBuffer(IntPtr backBuffer)

protected void InvalidateVideoImage()
{
GetSetVideoImageInvalid(true);

if (!m_renderOnCompositionTargetRendering)
InternalInvalidateVideoImage();
}
Expand All @@ -545,29 +558,39 @@ protected void InternalInvalidateVideoImage()
/* Ensure we run on the correct Dispatcher */
if(!D3DImage.Dispatcher.CheckAccess())
{
D3DImage.Dispatcher.Invoke((Action)(() => InvalidateVideoImage()));
D3DImage.Dispatcher.BeginInvoke((Action)(() => InternalInvalidateVideoImage()));
return;
}

/* If there is a new Surface to set,
* this method will do the trick */
SetBackBufferInternal(m_pBackBuffer);

// may save a few AddDirtyRect calls when the rendering thread is too busy
// or RenderOnCompositionTargetRendering is set but the video is not playing
bool invalid = GetSetVideoImageInvalid(false);
if (!invalid)
return;

/* Only render the video image if possible, or if IsRenderingEnabled is true */
if (D3DImage.IsFrontBufferAvailable && IsRenderingEnabled && m_pBackBuffer != IntPtr.Zero)
{
try
{
if (!D3DImage.TryLock(InvalidateVideoImageLockDuration))
return;
/* Invalidate the entire image */
D3DImage.Lock();
D3DImage.AddDirtyRect(new Int32Rect(0, /* Left */
0, /* Top */
D3DImage.PixelWidth, /* Width */
D3DImage.PixelHeight /* Height */));
D3DImage.Unlock();
}
catch (Exception)
{ }
finally
{
D3DImage.Unlock();
}
}

/* Invalidate all of our cloned D3DRenderers */
Expand All @@ -584,6 +607,19 @@ protected D3DRenderer()
Unloaded += D3DRendererUnloaded;
}

/// <summary>
/// TryLock timeout for the invalidate video image. Low values means higher UI responsivity, but more video dropped frames.
/// </summary>
public Duration InvalidateVideoImageLockDuration
{
get { return m_invalidateVideoImageLockDuration; }
set {
if (value == null)
throw new ArgumentNullException("InvalidateVideoImageLockDuration");
m_invalidateVideoImageLockDuration = value;
}
}

/// <summary>
/// Creates a clone of the D3DRenderer. This is a work for the visual
/// brush not working cross-threaded
Expand Down

0 comments on commit 7314b5e

Please sign in to comment.