Jump to content
  • Advertisement
Sign in to follow this  
arbitus

DX11 [SlimDX] Issues with KeyedMutex and WPF

This topic is 2549 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am porting my graphics system over to work inside a WPF application using the D3DImage, and I am running into an issue. My existing system uses DX11 for 3D rendering and Direct2D and DirectWrite to generate overlaid 2D content. This interop is handled via shared texture and coordinated with KeyedMutexes.

Currently, the port uses a modified version of the WpfSample10 D3DImageSlimDX to work with directX 11. Everything works fine when rendering the 3D scene. However, when I introduce the shared texture and keyedmutex, I get some strange behavior. It seems that any 3D commands issued after first acquiring the KeyedMutex for the texture only get executed roughly 50% of the frames, resulting in a blinking 3D scene. I have modified the WpfSample10 to show the minimum code necessary to reproduce this issue. Here is a video that show the blinking problem. If I move the KeyedMutex acquire and release until after the scene is rendered, or remove it altogether, everything works as seen here. I have tried several permutations, including drawing 3D objects before and after the acquire and release, and it always results in the same behavior: anything drawn after the acquire and release has about a 50% chance of showing up in the final frame.

The effect is easy to reproduce, just change the WpfSample10 to use a DirectX 11 device instead. Below are the changes I made to add the new shared texture and the keyedmutex, as well as the render code (I checked the return values for the acquire and release and it is always S_OK):

Initialization
[source lang="csharp"]
void InitD3D()
{
D3DDevice = new Direct3D11.Device(Direct3D11.DriverType.Hardware, Direct3D11.DeviceCreationFlags.Debug | Direct3D11.DeviceCreationFlags.BgraSupport, Direct3D11.FeatureLevel.Level_10_1);

Direct3D11.Texture2DDescription colordesc = new Direct3D11.Texture2DDescription();
colordesc.BindFlags = Direct3D11.BindFlags.RenderTarget | Direct3D11.BindFlags.ShaderResource;
colordesc.Format = DXGI.Format.B8G8R8A8_UNorm;
colordesc.Width = WindowWidth;
colordesc.Height = WindowHeight;
colordesc.MipLevels = 1;
colordesc.SampleDescription = new DXGI.SampleDescription(1, 0);
colordesc.Usage = Direct3D11.ResourceUsage.Default;
colordesc.OptionFlags = Direct3D11.ResourceOptionFlags.Shared;
colordesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
colordesc.ArraySize = 1;

Direct3D11.Texture2DDescription depthdesc = new Direct3D11.Texture2DDescription();
depthdesc.BindFlags = Direct3D11.BindFlags.DepthStencil;
depthdesc.Format = DXGI.Format.D32_Float_S8X24_UInt;
depthdesc.Width = WindowWidth;
depthdesc.Height = WindowHeight;
depthdesc.MipLevels = 1;
depthdesc.SampleDescription = new DXGI.SampleDescription(1, 0);
depthdesc.Usage = Direct3D11.ResourceUsage.Default;
depthdesc.OptionFlags = Direct3D11.ResourceOptionFlags.None;
depthdesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
depthdesc.ArraySize = 1;

Direct3D11.Texture2DDescription testdesc = new Direct3D11.Texture2DDescription();
testdesc.BindFlags = Direct3D11.BindFlags.RenderTarget | Direct3D11.BindFlags.ShaderResource;
testdesc.Format = DXGI.Format.B8G8R8A8_UNorm;
testdesc.Width = WindowWidth;
testdesc.Height = WindowHeight;
testdesc.MipLevels = 1;
testdesc.SampleDescription = new DXGI.SampleDescription(1, 0);
testdesc.Usage = Direct3D11.ResourceUsage.Default;
testdesc.OptionFlags = Direct3D11.ResourceOptionFlags.KeyedMutex;
testdesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
testdesc.ArraySize = 1;

using (var bytecode = D3DCompiler.ShaderBytecode.CompileFromFile(@"Shaders\MiniTri.fx", "fx_5_0", D3DCompiler.ShaderFlags.Debug, D3DCompiler.EffectFlags.None))
{
SampleEffect = new Direct3D11.Effect(D3DDevice, bytecode);
}

SharedTexture = new Direct3D11.Texture2D(D3DDevice, colordesc);
DepthTexture = new Direct3D11.Texture2D(D3DDevice, depthdesc);
//new texture to use for sharing if we can get the mutex issue resolved
TestTexture = new Direct3D11.Texture2D(D3DDevice, testdesc);
SampleRenderView = new Direct3D11.RenderTargetView(D3DDevice, SharedTexture);
SampleDepthView = new Direct3D11.DepthStencilView(D3DDevice, DepthTexture);

//new mutex defined as private class field
mutex = new DXGI.KeyedMutex(TestTexture);

//SampleEffect = Direct3D11.Effect.(D3DDevice, "MiniTri.fx", "fx_4_0");
Direct3D11.EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0); ;
Direct3D11.EffectPass pass = technique.GetPassByIndex(0);
SampleLayout = new Direct3D11.InputLayout(D3DDevice, pass.Description.Signature, new[] {
new Direct3D11.InputElement("POSITION", 0, DXGI.Format.R32G32B32A32_Float, 0, 0),
new Direct3D11.InputElement("COLOR", 0, DXGI.Format.R32G32B32A32_Float, 16, 0)
});

SampleStream = new DataStream(3 * 32, true, true);
SampleStream.WriteRange(new[] {
new Vector4(0.0f, 0.5f, 0.5f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.5f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.5f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f)
});
SampleStream.Position = 0;

SampleVertices = new Direct3D11.Buffer(D3DDevice, SampleStream, new Direct3D11.BufferDescription()
{
BindFlags = Direct3D11.BindFlags.VertexBuffer,
CpuAccessFlags = Direct3D11.CpuAccessFlags.None,
OptionFlags = Direct3D11.ResourceOptionFlags.None,
SizeInBytes = 3 * 32,
Usage = Direct3D11.ResourceUsage.Default
});

D3DDevice.ImmediateContext.Flush();
}
[/source]

Rendering
[source lang="csharp"]
public void Render(int arg)
{
D3DDevice.ImmediateContext.OutputMerger.SetTargets(SampleDepthView, SampleRenderView);
D3DDevice.ImmediateContext.Rasterizer.SetViewports(new Direct3D11.Viewport(0, 0, WindowWidth, WindowHeight, 0.0f, 1.0f));

D3DDevice.ImmediateContext.ClearDepthStencilView(SampleDepthView, Direct3D11.DepthStencilClearFlags.Depth | Direct3D11.DepthStencilClearFlags.Stencil, 1.0f, 0);
float c = ((float)(arg % 1000)) / 999.0f;
D3DDevice.ImmediateContext.ClearRenderTargetView(SampleRenderView, new SlimDX.Color4(1.0f, c, c, c));

D3DDevice.ImmediateContext.InputAssembler.InputLayout = SampleLayout;
D3DDevice.ImmediateContext.InputAssembler.PrimitiveTopology = Direct3D11.PrimitiveTopology.TriangleList;
D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new Direct3D11.VertexBufferBinding(SampleVertices, 32, 0));

mutex.Acquire(0, int.MaxValue);
mutex.Release(0);

Direct3D11.EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0);
Direct3D11.EffectPass pass = technique.GetPassByIndex(0);

for (int i = 0; i < technique.Description.PassCount; ++i)
{
pass.Apply(D3DDevice.ImmediateContext);
D3DDevice.ImmediateContext.Draw(3, 0);
}

D3DDevice.ImmediateContext.Flush();
}
[/source]

Any ideas?

Share this post


Link to post
Share on other sites
Advertisement
I tried the sample again on a different machine using feature level 11, and the problem persists, although it is much less noticeable (losing maybe 10% of frames).

Is there any reason why this should not conceptually work? Any suggestions on how I can synchronize DX11 and Direct2D some other way?

Edit: Let me clarify the usage scenario here...

According to this thread, the keyedmutex can be used to synchronize the access to a shared resource so that you can do the following:

mutexD3D10.Acquire(0, int.MaxValue);
// render direct2D stuff here
mutexD3D10.Release(1);

mutexD3D11.Acquire(1, int.MaxValue);
// render shared resource to 3D scene with direct3d 11
mutexD3D11.Release(0);

I have this working flawlessly in a standard winforms hosted 3D application. I am now porting my code to be used in a WPF D3DImage, and, as happens above, once the mutex is acquired on the Direct3D 11 resource, everything drawn afterwards in the frame prior to a device flush is randomly not drawn. In my case, it means my 2D overlay rapidly blinks overtop my 3D scene.

Share this post


Link to post
Share on other sites
I think we'll need more code to work out what the problem is. The code blocks from the first post don't show the usage scenario that you describe in the second post. Otherwise, it sounds like you have the right approach in mind. Be sure that the keyed mutex stuff is used throughout all usages of the shared keyed mutex resource:


create keyed mutex shared rendertarget in D3D11 or perhaps.
get D2D handle from the above render target.
get mutex for both versions of the render target



mutex10.acquire(0);
// D2D or other device rendering
mutex10.release(1);

mutex11.acquire(1);
// D3D11 rendering
// do one of the following
// 1. use as shader resource
// 2. copy to swapchain back buffer
// 3. call present if this is the swapchain's buffer
mutex11.release(0);

I don't know if you're having both render to the same shared render target or if one is rendering to it and the other is using it as a shader resource for drawing. Either way, provided that the resource always protected when being used there shouldn't be any sync issues as you're describing.

Share this post


Link to post
Share on other sites
The code in the first post is just a minimal reproduction of the issue. The actual usage scenario is a shared texture between a Direct3D 11 device and a Direct3D 10.1 device (created by Direct3D 11 and shared to Direct3D 10 via DXGI resource, exactly as presented in this thread). And then, after the 3D scene is rendered to the color buffer by Direct3D 11, I basically do what you have outlined, except I am not using a swap chain in this instance. That is because of the D3DImage interop, and sharing the color buffer with a Direct3D9Ex device, as per the WpfSample10, instead of presenting it. In my existing renderer based on a winform, everything works splendidly. I am under the assumption that even the code presented in the first post should "just work" and not have any issues.

So basically, if you look at the code in the first post, all I am doing is creating a texture with the keyed mutex resource option and a keyed mutex based on the texture resource. During the render call, I am clearing the color and depth buffers (in this case, both are textures/views, as the color buffer is shared with Direct3D9) and setting the input assembler data. Then I am simply acquiring and releasing the mutex before making the draw call, and then the device is flushed. Once the render method is complete, the D3DImage backbuffer is invalidated (this is part of the unmodified WpfSample10 code). During this process, I am doing nothing with the texture belonging to the mutex, simply acquiring and releasing the mutex. However, this has the effect shown in the videos in the first post, causing the drawn geometry to be missing from frames.

I will post the complete modified sample files below.

D3DImageSlimDX.cs
[source lang="csharp]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

using SlimDX.Direct3D9;

namespace DirectX11WpfTest
{
class D3DImageSlimDX : D3DImage, IDisposable
{
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();

static int NumActiveImages = 0;
static Direct3DEx D3DContext;
static DeviceEx D3DDevice;

Texture SharedTexture;

public D3DImageSlimDX()
{
InitD3D9();
NumActiveImages++;
}

public void Dispose()
{
SetBackBufferSlimDX(null);
if (SharedTexture != null)
{
SharedTexture.Dispose();
SharedTexture = null;
}

NumActiveImages--;
ShutdownD3D9();
}

public void InvalidateD3DImage()
{
if (SharedTexture != null)
{
Lock();
AddDirtyRect(new Int32Rect(0, 0, PixelWidth, PixelHeight));
Unlock();
}
}

public void SetBackBufferSlimDX(SlimDX.Direct3D11.Texture2D Texture)
{
if (SharedTexture != null)
{
SharedTexture.Dispose();
SharedTexture = null;
}

if (Texture == null)
{
if (SharedTexture != null)
{
SharedTexture = null;
Lock();
SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
Unlock();
}
}
else if (IsShareable(Texture))
{
Format format = TranslateFormat(Texture);
if (format == Format.Unknown)
throw new ArgumentException("Texture format is not compatible with OpenSharedResource");

IntPtr Handle = GetSharedHandle(Texture);
if (Handle == IntPtr.Zero)
throw new ArgumentNullException("Handle");

SharedTexture = new Texture(D3DDevice, Texture.Description.Width, Texture.Description.Height, 1, Usage.RenderTarget, format, Pool.Default, ref Handle);
using (Surface Surface = SharedTexture.GetSurfaceLevel(0))
{
Lock();
SetBackBuffer(D3DResourceType.IDirect3DSurface9, Surface.ComPointer);
Unlock();
}
}
else
throw new ArgumentException("Texture must be created with ResourceOptionFlags.Shared");
}

void InitD3D9()
{
if (NumActiveImages == 0)
{
D3DContext = new Direct3DEx();

PresentParameters presentparams = new PresentParameters();
presentparams.Windowed = true;
presentparams.SwapEffect = SwapEffect.Discard;
presentparams.DeviceWindowHandle = GetDesktopWindow();
presentparams.PresentationInterval = PresentInterval.Immediate;

D3DDevice = new DeviceEx(D3DContext, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, presentparams);
}
}

void ShutdownD3D9()
{
if (NumActiveImages == 0)
{
if (SharedTexture != null)
{
SharedTexture.Dispose();
SharedTexture = null;
}

if (D3DDevice != null)
{
D3DDevice.Dispose();
D3DDevice = null;
}

if (D3DContext != null)
{
D3DContext.Dispose();
D3DContext = null;
}
}
}

IntPtr GetSharedHandle(SlimDX.Direct3D11.Texture2D Texture)
{
SlimDX.DXGI.Resource resource = new SlimDX.DXGI.Resource(Texture);
IntPtr result = resource.SharedHandle;

resource.Dispose();

return result;
}

Format TranslateFormat(SlimDX.Direct3D11.Texture2D Texture)
{
switch (Texture.Description.Format)
{
case SlimDX.DXGI.Format.R10G10B10A2_UNorm:
return SlimDX.Direct3D9.Format.A2B10G10R10;

case SlimDX.DXGI.Format.R16G16B16A16_Float:
return SlimDX.Direct3D9.Format.A16B16G16R16F;

case SlimDX.DXGI.Format.B8G8R8A8_UNorm:
return SlimDX.Direct3D9.Format.A8R8G8B8;

default:
return SlimDX.Direct3D9.Format.Unknown;
}
}

bool IsShareable(SlimDX.Direct3D11.Texture2D Texture)
{
return (Texture.Description.OptionFlags & SlimDX.Direct3D11.ResourceOptionFlags.Shared) != 0;
}
}
}
[/source]

Scene.cs
[source lang="csharp"]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using SlimDX;
using DXGI = SlimDX.DXGI;
using Direct3D11 = SlimDX.Direct3D11;
using D3DCompiler = SlimDX.D3DCompiler;

namespace DirectX11WpfTest
{
class Scene : IDisposable
{
Direct3D11.Device D3DDevice;
DataStream SampleStream;
Direct3D11.InputLayout SampleLayout;
Direct3D11.Buffer SampleVertices;
Direct3D11.RenderTargetView SampleRenderView;
Direct3D11.DepthStencilView SampleDepthView;
Direct3D11.Effect SampleEffect;
Direct3D11.Texture2D DepthTexture;
int WindowWidth;
int WindowHeight;

private Direct3D11.Texture2D TestTexture;
private DXGI.KeyedMutex mutex;

public Direct3D11.Texture2D SharedTexture
{
get;
set;
}

public Scene()
{
WindowWidth = 100;
WindowHeight = 100;
InitD3D();
}

public void Dispose()
{
DestroyD3D();
}

public void Render(int arg)
{
D3DDevice.ImmediateContext.OutputMerger.SetTargets(SampleDepthView, SampleRenderView);
D3DDevice.ImmediateContext.Rasterizer.SetViewports(new Direct3D11.Viewport(0, 0, WindowWidth, WindowHeight, 0.0f, 1.0f));

D3DDevice.ImmediateContext.ClearDepthStencilView(SampleDepthView, Direct3D11.DepthStencilClearFlags.Depth | Direct3D11.DepthStencilClearFlags.Stencil, 1.0f, 0);
float c = ((float)(arg % 1000)) / 999.0f;
D3DDevice.ImmediateContext.ClearRenderTargetView(SampleRenderView, new SlimDX.Color4(1.0f, c, c, c));

D3DDevice.ImmediateContext.InputAssembler.InputLayout = SampleLayout;
D3DDevice.ImmediateContext.InputAssembler.PrimitiveTopology = Direct3D11.PrimitiveTopology.TriangleList;
D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new Direct3D11.VertexBufferBinding(SampleVertices, 32, 0));

mutex.Acquire(0, int.MaxValue);
mutex.Release(0);

Direct3D11.EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0);
Direct3D11.EffectPass pass = technique.GetPassByIndex(0);

for (int i = 0; i < technique.Description.PassCount; ++i)
{
pass.Apply(D3DDevice.ImmediateContext);
D3DDevice.ImmediateContext.Draw(3, 0);
}

D3DDevice.ImmediateContext.Flush();
}

void InitD3D()
{
D3DDevice = new Direct3D11.Device(Direct3D11.DriverType.Hardware, Direct3D11.DeviceCreationFlags.Debug | Direct3D11.DeviceCreationFlags.BgraSupport, Direct3D11.FeatureLevel.Level_10_1);

Direct3D11.Texture2DDescription colordesc = new Direct3D11.Texture2DDescription();
colordesc.BindFlags = Direct3D11.BindFlags.RenderTarget | Direct3D11.BindFlags.ShaderResource;
colordesc.Format = DXGI.Format.B8G8R8A8_UNorm;
colordesc.Width = WindowWidth;
colordesc.Height = WindowHeight;
colordesc.MipLevels = 1;
colordesc.SampleDescription = new DXGI.SampleDescription(1, 0);
colordesc.Usage = Direct3D11.ResourceUsage.Default;
colordesc.OptionFlags = Direct3D11.ResourceOptionFlags.Shared;
colordesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
colordesc.ArraySize = 1;

Direct3D11.Texture2DDescription depthdesc = new Direct3D11.Texture2DDescription();
depthdesc.BindFlags = Direct3D11.BindFlags.DepthStencil;
depthdesc.Format = DXGI.Format.D32_Float_S8X24_UInt;
depthdesc.Width = WindowWidth;
depthdesc.Height = WindowHeight;
depthdesc.MipLevels = 1;
depthdesc.SampleDescription = new DXGI.SampleDescription(1, 0);
depthdesc.Usage = Direct3D11.ResourceUsage.Default;
depthdesc.OptionFlags = Direct3D11.ResourceOptionFlags.None;
depthdesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
depthdesc.ArraySize = 1;

Direct3D11.Texture2DDescription testdesc = new Direct3D11.Texture2DDescription();
testdesc.BindFlags = Direct3D11.BindFlags.RenderTarget | Direct3D11.BindFlags.ShaderResource;
testdesc.Format = DXGI.Format.B8G8R8A8_UNorm;
testdesc.Width = WindowWidth;
testdesc.Height = WindowHeight;
testdesc.MipLevels = 1;
testdesc.SampleDescription = new DXGI.SampleDescription(1, 0);
testdesc.Usage = Direct3D11.ResourceUsage.Default;
testdesc.OptionFlags = Direct3D11.ResourceOptionFlags.KeyedMutex;
testdesc.CpuAccessFlags = Direct3D11.CpuAccessFlags.None;
testdesc.ArraySize = 1;

using (var bytecode = D3DCompiler.ShaderBytecode.CompileFromFile(@"Shaders\MiniTri.fx", "fx_5_0", D3DCompiler.ShaderFlags.Debug, D3DCompiler.EffectFlags.None))
{
SampleEffect = new Direct3D11.Effect(D3DDevice, bytecode);
}

SharedTexture = new Direct3D11.Texture2D(D3DDevice, colordesc);
DepthTexture = new Direct3D11.Texture2D(D3DDevice, depthdesc);
TestTexture = new Direct3D11.Texture2D(D3DDevice, testdesc);
SampleRenderView = new Direct3D11.RenderTargetView(D3DDevice, SharedTexture);
SampleDepthView = new Direct3D11.DepthStencilView(D3DDevice, DepthTexture);

mutex = new DXGI.KeyedMutex(TestTexture);

//SampleEffect = Direct3D11.Effect.(D3DDevice, "MiniTri.fx", "fx_4_0");
Direct3D11.EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0); ;
Direct3D11.EffectPass pass = technique.GetPassByIndex(0);
SampleLayout = new Direct3D11.InputLayout(D3DDevice, pass.Description.Signature, new[] {
new Direct3D11.InputElement("POSITION", 0, DXGI.Format.R32G32B32A32_Float, 0, 0),
new Direct3D11.InputElement("COLOR", 0, DXGI.Format.R32G32B32A32_Float, 16, 0)
});

SampleStream = new DataStream(3 * 32, true, true);
SampleStream.WriteRange(new[] {
new Vector4(0.0f, 0.5f, 0.5f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.5f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.5f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f)
});
SampleStream.Position = 0;

SampleVertices = new Direct3D11.Buffer(D3DDevice, SampleStream, new Direct3D11.BufferDescription()
{
BindFlags = Direct3D11.BindFlags.VertexBuffer,
CpuAccessFlags = Direct3D11.CpuAccessFlags.None,
OptionFlags = Direct3D11.ResourceOptionFlags.None,
SizeInBytes = 3 * 32,
Usage = Direct3D11.ResourceUsage.Default
});

D3DDevice.ImmediateContext.Flush();
}

void DestroyD3D()
{
if (SampleVertices != null)
{
SampleVertices.Dispose();
SampleVertices = null;
}

if (SampleLayout != null)
{
SampleLayout.Dispose();
SampleLayout = null;
}

if (SampleEffect != null)
{
SampleEffect.Dispose();
SampleEffect = null;
}

if (SampleRenderView != null)
{
SampleRenderView.Dispose();
SampleRenderView = null;
}

if (SampleDepthView != null)
{
SampleDepthView.Dispose();
SampleDepthView = null;
}

if (SampleStream != null)
{
SampleStream.Dispose();
SampleStream = null;
}

if (SampleLayout != null)
{
SampleLayout.Dispose();
SampleLayout = null;
}

if (SharedTexture != null)
{
SharedTexture.Dispose();
SharedTexture = null;
}

if (TestTexture != null)
{
TestTexture.Dispose();
TestTexture = null;
}

if (DepthTexture != null)
{
DepthTexture.Dispose();
DepthTexture = null;
}

if (mutex != null)
{
mutex.Dispose();
mutex = null;
}

if (D3DDevice != null)
{
D3DDevice.Dispose();
D3DDevice = null;
}
}
}
}
[/source]

Codebehind for MainWindow.xaml
[source lang="csharp"]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DirectX11WpfTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
timer = new Stopwatch();
Loaded += Window_Loaded;
Closing += Window_Closing;
InitializeComponent();
}

private Stopwatch timer;
private Scene scene;
private D3DImageSlimDX slimDXImage;

private void Window_Loaded(object sender, RoutedEventArgs e)
{
slimDXImage = new D3DImageSlimDX();
slimDXImage.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;

SlimDXImage.Source = slimDXImage;

scene = new Scene();

slimDXImage.SetBackBufferSlimDX(scene.SharedTexture);
BeginRenderingScene();
}

private void Window_Closing(object sender, CancelEventArgs e)
{
if (slimDXImage != null)
{
slimDXImage.Dispose();
slimDXImage = null;
}

if (scene != null)
{
scene.Dispose();
scene = null;
}
}

void OnRendering(object sender, EventArgs e)
{
scene.Render(timer.Elapsed.Milliseconds);
slimDXImage.InvalidateD3DImage();
}

void BeginRenderingScene()
{
if (slimDXImage.IsFrontBufferAvailable)
{
foreach (var item in SlimDX.ObjectTable.Objects)
{
}

SlimDX.Direct3D11.Texture2D Texture = scene.SharedTexture;
slimDXImage.SetBackBufferSlimDX(Texture);
CompositionTarget.Rendering += OnRendering;

timer.Start();
}
}

void StopRenderingScene()
{
timer.Stop();
CompositionTarget.Rendering -= OnRendering;
}

void OnIsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// This fires when the screensaver kicks in, the machine goes into sleep or hibernate
// and any other catastrophic losses of the d3d device from WPF's point of view
if (slimDXImage.IsFrontBufferAvailable)
{
BeginRenderingScene();
}
else
{
StopRenderingScene();
}
}
}
}
[/source]

Share this post


Link to post
Share on other sites
So I'm not sure about this approach to using D3D11 with DX9. It looks as though the DX11 call to flush is expected to block and therefore enforce synchronization where all DX11 content will be rendered before proceeding. However, as described here the call to flush is asynchronous. It may or may not return before all rendering is actually done. It looks like the lock/release on the other resource has enough overhead to cause flush to return before the triangle is drawn. When that happens, a small percentage of the time, D3D9 presents the incomplete frame.

The main trouble is that the shared resource on the DX11 size has no locking mechanism to ensure that it completes before dx9 grabs the data. And since flush doesn't block until rendering is guaranteed to be complete your left with having to poll the device with a query instead, which will eat time. This is a good example for why the keyed mutex approach was added.


Share this post


Link to post
Share on other sites

So I'm not sure about this approach to using D3D11 with DX9. It looks as though the DX11 call to flush is expected to block and therefore enforce synchronization where all DX11 content will be rendered before proceeding. However, as described here the call to flush is asynchronous. It may or may not return before all rendering is actually done. It looks like the lock/release on the other resource has enough overhead to cause flush to return before the triangle is drawn. When that happens, a small percentage of the time, D3D9 presents the incomplete frame.

The main trouble is that the shared resource on the DX11 size has no locking mechanism to ensure that it completes before dx9 grabs the data. And since flush doesn't block until rendering is guaranteed to be complete your left with having to poll the device with a query instead, which will eat time. This is a good example for why the keyed mutex approach was added.


I was starting to arrive at a similar conclusion when I was typing the last post, but I was not sure if the flush command was synchronous or not. However, the problem is that I can render a pretty complex scene and have the full results displayed without any problematic frames as long as I don't include the mutex code. Also, this method (minus the mutex acquire and release) seems to be a pretty popular way of integrating DX10+ with D3DImage and I have not seen anything previously about synchronization issues.

Do you have a recommendation on how I might be able to force synchronization? You mentioned polling the device with a query, but I have never had to do this before, so any advice on how to get started?

Share this post


Link to post
Share on other sites
My speculation for the flush command is that the runtime or drive may see the acquire/release as being very costly and so return early rather than blocking the caller for such a long period of time. Other basic commands may not suffer from this. I honestly couldn't say for sure, but the documentation does say it's not a reliable mechanism. Perhaps in practice most people find it to be reliable, but it'll not be guaranteed, even between release or patches.


The query is just a simple object that can indicate when a command has finished. You'll create an ID3D11Query object using the ID3D11:CreateQuery method and use a description with the D3D11_QUERY_EVENT flag. Then in the code you will call ID3D11DeviceContext::End() with the query object once the drawing of all D3D11 context is complete. Now, before DX9 can continue with it's work, you'll have to continue to check ID3D11DeviceContext::GetData() until a result of TRUE is returned. This function will return S_OK and the out data which is a bool should read TRUE. If the out value is false it implies that rendering has not completed. This call let's you check the status immediately, or it can block and flush the pipeline until the result will return TRUE. TRUE indicates that the pipeline has completed the query and all prior submitted D3D commands.

Spinning on this will be costly and could drive CPU usage to 100% so I recommend either making the blocking call or having the thread do some other work and checking the result periodically.

Share this post


Link to post
Share on other sites
I added the following code to the end of the render method (not concerned about dealing with the effects of spinning until I solve this issue). I assume this is how GetData should be used in SlimDX. The problem still persists.


[source lang="csharp"]

Query query = new Query(D3DDevice, new QueryDescription
{
Flags = QueryFlags.None,
Type = QueryType.Event
});

D3DDevice.ImmediateContext.End(query);

while (!D3DDevice.ImmediateContext.GetData<bool>(query))
{
System.Diagnostics.Debug.WriteLine("Waiting...");
}

query.Dispose();

[/source]



Share this post


Link to post
Share on other sites
You need to add the Begin(query) command as well before you start drawing. After making those modifications, the flickering seems to go away for me.

Share this post


Link to post
Share on other sites

You need to add the Begin(query) command as well before you start drawing. After making those modifications, the flickering seems to go away for me.



Would you mind posting your render method? I put the Begin(query) in mine as well, and I still have flickering. Strangely, if I change the conditional on the spinning while loop, it seems to diminish the flickering.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!