Jump to content

  • Log In with Google      Sign In   
  • Create Account

[SlimDX] Issues with KeyedMutex and WPF


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
15 replies to this topic

#1 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 12 July 2011 - 09:33 AM

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?

Sponsor:

#2 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 14 July 2011 - 08:30 AM

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.

#3 DieterVW   Members   -  Reputation: 700

Like
0Likes
Like

Posted 15 July 2011 - 11:09 AM

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.

#4 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 15 July 2011 - 11:53 AM

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]

#5 DieterVW   Members   -  Reputation: 700

Like
0Likes
Like

Posted 15 July 2011 - 12:31 PM

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.




#6 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 15 July 2011 - 12:56 PM

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?

#7 DieterVW   Members   -  Reputation: 700

Like
0Likes
Like

Posted 15 July 2011 - 02:08 PM

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.

#8 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 15 July 2011 - 04:01 PM

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]





#9 Mike.Popoloski   Crossbones+   -  Reputation: 2911

Like
0Likes
Like

Posted 17 July 2011 - 11:19 AM

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.
Mike Popoloski | Journal | SlimDX

#10 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 17 July 2011 - 02:32 PM

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.

#11 Mike.Popoloski   Crossbones+   -  Reputation: 2911

Like
0Likes
Like

Posted 17 July 2011 - 03:18 PM


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.


[source lang="csharp"]public void Render(int arg){ var query = new Query(actualDevice, new QueryDescription(QueryType.Event, QueryFlags.None)); D3DDevice.ClearDepthStencilView(SampleDepthView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0); float c = ((float)(arg % 1000)) / 999.0f; D3DDevice.ClearRenderTargetView(SampleRenderView, new SlimDX.Color4(1.0f, c, c, c)); D3DDevice.Begin(query); D3DDevice.InputAssembler.InputLayout = SampleLayout; D3DDevice.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; D3DDevice.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(SampleVertices, 32, 0)); mutex.Acquire(0, int.MaxValue); mutex.Release(0); EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0); EffectPass pass = technique.GetPassByIndex(0); for (int i = 0; i < technique.Description.PassCount; ++i) { pass.Apply(D3DDevice); D3DDevice.Draw(3, 0); } D3DDevice.End(query); while (!D3DDevice.GetData<bool>(query)) System.Diagnostics.Debug.WriteLine("Hello!"); query.Dispose();}[/source]
Mike Popoloski | Journal | SlimDX

#12 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 17 July 2011 - 04:48 PM

Sigh, mine is still happily blinking. :angry:

#13 Mike.Popoloski   Crossbones+   -  Reputation: 2911

Like
0Likes
Like

Posted 18 July 2011 - 04:00 PM

Sigh, mine is still happily blinking. :angry:


Try zipping up your whole project and sending it to me. I'll try debugging it on another computer and see if the problem manifests there as well.
Mike Popoloski | Journal | SlimDX

#14 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 26 July 2011 - 09:08 PM

After further investigations, I have decided to resurrect this thread with new information. My suspicions are that this incredibly contrived use case might be manifesting a bug.

I brought this to the attention of a DX team member, and he directed me here. I had already seen something similar here, integrating Diret2D and WPF. I dove into the SurfaceQueue code and found that it contained two synchronization mechanisms. One is for multithreaded enqueue/dequeue of surfaces, which does nothing in my use case, as I am simply producing a surface that is to be immediately consumed. The second is the render synchronization which is intended to force the frame to finish rendering synchronously with the enqueue of the surface.

The magic is in the enqueue method of the SurfaceQueue:

// Copy a small portion of the surface onto the staging surface
hr = m_pProducer->GetDevice()->CopySurface(pStagingResource, pSurface, width, height);
...
//
// Force rendering to complete by locking the staging resource.
//
if (FAILED(hr = m_pProducer->GetDevice()->LockSurface(pStagingResource, Flags)))
{
	goto end;
}
if (FAILED(hr = m_pProducer->GetDevice()->UnlockSurface(pStagingResource)))
{
	goto end;
}
ASSERT(QueueEntry.pStagingResource == NULL); 
//
// The call to lock the surface completed succesfully meaning the surface if flushed
// and ready for dequeue.  Mark the surface as such and add it to the fifo queue.
//


In a nutshell, the SurfaceQueue copies a portion of the surface to be enqueued to a staging resource, and then (in the case of Direct3D10 and 11), maps and unmaps the resource into CPU space. In theory, this should force the rendering to the original surface to complete so that the staging resource will be up to date when it is (potentially) read in CPU space. This makes sense, so armed with this information, I added the following code to my previous example's render method:

[source lang="csharp"]D3DDevice.ImmediateContext.CopyResource(SharedTexture, StagingTexture);var data = D3DDevice.ImmediateContext.MapSubresource(StagingTexture, 0, StagingTexture.Description.Width * StagingTexture.Description.Height * sizeof(float), MapMode.Read, MapFlags.None);D3DDevice.ImmediateContext.UnmapSubresource(StagingTexture, 0);[/source]

and initialization:
[source lang="csharp"]Texture2DDescription stagingdesc = new Texture2DDescription();stagingdesc.BindFlags = BindFlags.None;stagingdesc.Format = DXGI.Format.B8G8R8A8_UNorm;stagingdesc.Width = WindowWidth;stagingdesc.Height = WindowHeight;stagingdesc.MipLevels = 1;stagingdesc.SampleDescription = new DXGI.SampleDescription(1, 0);stagingdesc.Usage = ResourceUsage.Staging;stagingdesc.OptionFlags = ResourceOptionFlags.None;stagingdesc.CpuAccessFlags = CpuAccessFlags.Read;stagingdesc.ArraySize = 1;StagingTexture = new Texture2D(D3DDevice, stagingdesc);[/source]

Running the sample results in the same blinking behavior as before. In addition, i was playing around with the original Direct3D10 sample, and added Direct2D code to clear the render target using the SharedTexture surface, and that resulted in the behavior I had previously noted where the any 3D content before the Acquire call to the KeyedMutex would complete 100% of the time, but anything after that would blink. Except in this case, any Direct2D content drawn after the 3D triangle would blink, while the triangle was always present. So, I remade the current sample to add an additional triangle. Code follows:

Initialization:
[source lang="csharp"]SampleStream1 = new DataStream(3 * 32, true, true);SampleStream1.WriteRange(new[] { new Vector4(0.25f, 0.5f, 0.5f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), new Vector4(0.75f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), new Vector4(-0.25f, -0.5f, 0.5f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f)});SampleStream1.Position = 0;SampleVertices1 = new Buffer(D3DDevice, SampleStream1, new BufferDescription(){ BindFlags = BindFlags.VertexBuffer, CpuAccessFlags = CpuAccessFlags.None, OptionFlags = ResourceOptionFlags.None, SizeInBytes = 3 * 32, Usage = ResourceUsage.Default});[/source]

And the new completed Render:
[source lang="csharp"]D3DDevice.ImmediateContext.ClearDepthStencilView(SampleDepthView, DepthStencilClearFlags.Depth | 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 = PrimitiveTopology.TriangleList;D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(SampleVertices, 32, 0));EffectTechnique technique = SampleEffect.GetTechniqueByIndex(0);EffectPass pass = technique.GetPassByIndex(0);for (int i = 0; i < technique.Description.PassCount; ++i){ pass.Apply(D3DDevice.ImmediateContext); D3DDevice.ImmediateContext.Draw(3, 0);}Mutex.Acquire(0, int.MaxValue);Mutex.Release(0);D3DDevice.ImmediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(SampleVertices1, 32, 0));for (int i = 0; i < technique.Description.PassCount; ++i){ pass.Apply(D3DDevice.ImmediateContext); D3DDevice.ImmediateContext.Draw(3, 0);}D3DDevice.ImmediateContext.CopyResource(SharedTexture, StagingTexture);var data = D3DDevice.ImmediateContext.MapSubresource(StagingTexture, 0, StagingTexture.Description.Width * StagingTexture.Description.Height * sizeof(float), MapMode.Read, MapFlags.None);data.Data.Position = 3377 * 4;var read1 = data.Data.ReadByte();var read2 = data.Data.ReadByte();var read3 = data.Data.ReadByte();var read4 = data.Data.ReadByte();System.Diagnostics.Debug.WriteLine("Actual1: " + read1 + " " + read2 + " " + read3 + " " + read4);data.Data.Position = 3390 * 4;read1 = data.Data.ReadByte();read2 = data.Data.ReadByte();read3 = data.Data.ReadByte();read4 = data.Data.ReadByte();System.Diagnostics.Debug.WriteLine("Actual2: " + read1 + " " + read2 + " " + read3 + " " + read4);System.Diagnostics.Debug.WriteLine("");D3DDevice.ImmediateContext.UnmapSubresource(StagingTexture, 0);[/source]

First of all, I know this is stupid, but how can I find the pitch so that I can request the correct data size?
Aside from that, there are a few things to note here (it only takes a few minutes to set the sample up and it is interesting to say the least):
1) The second triangle will flicker, while the original triangle will remain solid. This is expected though not desired.
2) Those data readings represent the near tip of each triangle (although I am not sure if this is uniform across all hardware due to pitch). Every frame, my debug output reads thusly:
Actual1: 6 1 247 255
Actual2: 4 4 247 255

This means that every frame, by the end of the execution of the Render method, the staging surface clearly has the data showing that both triangles are drawn (I have also created additional tests sampling additional pixels to confirm this is absolutely true). I must assume that the original shared surface has the triangles drawn as well because the staging surface is a copy. Immediately following the render method is the synchronous lock the D3DImage version of the surface and its supposed copy to an image source (the bowels of the D3DImage). Yet this does not seem possible, because visually, some frames clearly do not have the second triangle visible, although tests show that every frame it is being produced prior to the D3DImage locking and copying it.

So if the map/unmap is forcing synchronization as the SurfaceQueue suggests (and my CPU read tests), I am at a loss as to why this does not work. If this were a synchronization issue with DX9Ex, why is the first triangle ALWAYS visible? Am I missing something?

#15 Mike.Popoloski   Crossbones+   -  Reputation: 2911

Like
0Likes
Like

Posted 26 July 2011 - 09:38 PM

We changed MapSubresource in the current build of SlimDX to not require a size, since like you've realized knowing the pitch beforehand is pretty much impossible.

As for your actual issue, I don't really know what's going on there, and I was unable to make it work properly. Maybe it really is just broken for this use case.
Mike Popoloski | Journal | SlimDX

#16 arbitus   Members   -  Reputation: 436

Like
0Likes
Like

Posted 26 July 2011 - 09:44 PM

We changed MapSubresource in the current build of SlimDX to not require a size, since like you've realized knowing the pitch beforehand is pretty much impossible.


Well thanks for this at least, I was really scratching my head.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS