Sign in to follow this  
daVinci

How to use D2D with D3D11?

Recommended Posts

KawaDriver    100
I'm trying to combine direct3d 11 and direct2d too. Actually I have trouble with OpenSharedResource. This method always returns E_INVALIDARG. My code looks like this:

D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(D3D11_TEXTURE2D_DESC));

texDesc.Width = swapChainDesc.BufferDesc.Width;
texDesc.Height = swapChainDesc.BufferDesc.Height;
texDesc.Format = swapChainDesc.BufferDesc.Format;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.SampleDesc.Count = swapChainDesc.SampleDesc.Count;
texDesc.SampleDesc.Quality = swapChainDesc.SampleDesc.Quality;
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
texDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;

_pD3D11Device->CreateTexture2D(&texDesc, NULL, &_pD3D11Texture);
_pD3D11Texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void**)&_pMutex);

IDXGIResource* pResource;
_pD3D11Texture->QueryInterface(__uuidof(IDXGIResource), (void**)&pResource);

HANDLE hResource;
HRESULT hResult = pResource->GetSharedHandle(&hResource);

ID3D10Resource* pTexture;
hResult = _pD3D10Device->OpenSharedResource(hResource, __uuidof(ID3D10Resource), (void**)&pTexture);

I created the direct3d 11 device with no creation flags. The direct3d 10.1 device was created with the flag D3D10_CREATE_DEVICE_BGRA_SUPPORT. Any ideas?

Share this post


Link to post
Share on other sites
KawaDriver    100
I don't use multisampling. Count is set to one and Quality is set to zero. I also tried to set the format of the texture to B8G8R8A8_UNORM. However, the OpenSharedResource function fails...

maybe it is a problem with my two devices? I created each device with NULl as argument for the adapter.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901
Yes, you need to use the same adapter, NULL doesn't work. I noticed this myself when I first tried it. =)
The documentation says that NULL means the same adapter as the first one returned from IDXGIFactory::EnumAdapters. So use CreateDXGIFactory and EnumAdapters(0, &pAdapter) to obtain the adapter. Or it might work by creating the D3D11 device with NULL and then checking which adapter it uses.

Share this post


Link to post
Share on other sites
GPUShader    100
Is it absolutely not possible to share the backbuffer between a D3D10.1 and D3D11 device? I have tried all kinds of things and it just won't work. I really hope MS creates a D3D11 version of D2D soon, as I really can't use the render-to-texture-and-blend trick for this program I am working on.

IDXGIResource *DX11Resource = NULL;
EIF(g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&DX11Resource)));

HANDLE DX11ResourceHandle = NULL;
EIF(DX11Resource->GetSharedHandle(&DX11ResourceHandle));

The GetSharedHandle call fails with DXGI_ERROR_NOT_FOUND, and I tried creating the swapchain in various ways with the DXGI_USAGE_SHARED flag.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901
You can use the HDC method instead, with a GDI compatible back-buffer, which lets you use D2D directly on the D3D11 target (through an ID2D1DCRenderTarget). You will have to use software mode for D2D though, but unless you draw a lot of text it might be acceptable, it's not all that slow.

Share this post


Link to post
Share on other sites
GPUShader    100
Thanks for the tip, but unfortunately this will not work well as I am doing a lot of rendering with D2D for UI controls and performance graphs.

I thought the whole point of D2D dealing with D3D through DXGI was to avoid problems like this? How frustrating! Oh well, here's to hoping this is fixed soon.

Share this post


Link to post
Share on other sites
DieterVW    724
GPUShader: Why do you need direct sharing of the swap chain created backbuffer? Though it would certainly be more convenient -- this current method isn't very limiting. I'd like to understand the scenario you see where using a separate render to texture and final composite won't work. I've seen several applications using D2D as an overlay UI and they all worked quite well.

Share this post


Link to post
Share on other sites
GPUShader    100
In one of my applications, I am rendering small info boxes with D2D with text inside of each one; but each one also has some D3D content placed within it. This requires that I switch between D2D and D3D a number of times, and so I would not be able to simply perform a single composite to combine the D2D and D3D content.

I suppose what I'll have to do (for now) is perform multiple composites from a temporary texture.

Share this post


Link to post
Share on other sites
GPUShader    100
The whole reason why I am porting my application to D3D11 is to move the visualization part (a reaction-diffusion system) into a compute shader. And due to the way I am using shared memory, it has to be SM 5.0 I'm afraid. :(

I'd like to thank you guys for your quick replies. I think what I'll do is just render the D2D content into a temporary texture and composite it to the backbuffer multiple times per frame for the information boxes -- I think this should still be quite performant.

Share this post


Link to post
Share on other sites
JB2009    100
Quote:
DieterVW wrote: Remember that in D2D and D3D the commands are queued, and run async from the thread that is calling the API. In your case, with 2 devices, they are probably fighting over who gets the lock first, since both are asking for lock zero. ...

Release the D2D lock with 1, and aquire the d3d lock with 1, that way you've forced and order.


In my situation, mutexes are controlling access to the shared resource (i.e. the 2D texture rendered to using D2D1 by the D3D10.1 device and read (composited) by the D3D11 device) by each 3D device, whereas the GDI is writing to the D3D11 back buffer directly and does not use the shared texture resource at all.

Are you certain that the mutex key values are relevant for this situation? (In other words, I deduced that the key values relate to the resource being shared, whereas from your description above, it sounds like you think that they relate to the device(s)). Without knowing more about how GDI writes to the D3D11 backbuffer without colliding with the D3D11 device, I don't have the information to answer this.

JB.

Share this post


Link to post
Share on other sites
DieterVW    724
JB:

Can you check the error message in the exception that is thrown when using the debug device? From the sounds of it, something in the D3D11 pipeline is set incorrectly, (possibly NULL, or maybe a random value). The debug device will throw an exception when it hits an egregious error, often an unexpected NULL. The exception should contain information about the error. Alternatively, you could use the ID3D11Debug or ID3D11InfoQueue to get the messages about the error that way. Without the debug device, D3D will just ignore the draw call since it detected an error when validating the pipeline.


With respect to the mutex, The pseudo algorithm you wrote does have D2D and D3D competing for the lock, but your right, the manifestation of the bug isn't what I suggested. I think in this instance your composite from D2D would just be 1 frame late since the 3D device would do the composite before D2D did the drawing.
The Mutex lock is important when doing either a read or a write.

I see no issue with the way you're using GDI here, I presume, as you do, that it'll work fine.

EDIT: Spelling

[Edited by - DieterVW on September 30, 2009 12:09:16 PM]

Share this post


Link to post
Share on other sites
JB2009    100
Quote:
GPUShader: I thought the whole point of D2D dealing with D3D through DXGI was to avoid problems like this? How frustrating! Oh well, here's to hoping this is fixed soon.


I'm in the same situation as GPUShader - needing high performance 2D interleaved with 3D, and needing to use compute shaders and hence D3D11.

Although DieterVW and Erik have been very helpful, I am updating a big graphics library, and a huge suite of software that uses the library, and really don't want to do the job twice.

I can't even find out whether a version of D2D1 that is compatible with D3D11 is on MS's roadmap (although it is hard to believe that it is not), let alone release date estimates.

Is there any way to put these requests/questions to MS's DirectX team?

JB.

Share this post


Link to post
Share on other sites
Erik Rufelt    5901
If you only want text, then you can always draw the required character-space to a texture at initialization, and then use that to draw the text. Though it's not very good if the font/size range is large.

Share this post


Link to post
Share on other sites
DieterVW    724
Quote:
Original post by JB2009
Is there any way to put these requests/questions to MS's DirectX team?.


As a member of the DX team, I can tell you that the issues are already known and well understood.

Share this post


Link to post
Share on other sites
JB2009    100
Quote:
DieterVW: As a member of the DX team, I can tell you that the issues are already known and well understood.


DieterVW,

Many thanks - I did not know that you were a member of the DX team (though I guessed you might be).

Quote:
DieterVW: You will likely get quite a bit of use from this method before something else comes along.


Is there any more information available at this stage? At this point I don't know whether we are talking weeks, months or years. (For project planning purposes, it would have been helpful if the Aug 2009 SDK included a list of what was not yet completed or working. It seems silent on the subject).

JB.

Share this post


Link to post
Share on other sites
JB2009    100
Quote:
DieterVW: Can you check the error message in the exception that is thrown when using the debug device? From the sounds of it, something in the D3D11 pipeline is set incorrectly, (possibly NULL, or maybe a random value). The debug device will throw an exception when it hits an egregious error, often an unexpected NULL. The exception should containly information about the error. Alternatively, you could use the ID3D11Debug or ID3D11InfoQueue to get the messages about the error that way. Without the debug device, D3D will just ignore the draw call since it detected an error when validating the pipeline.


DieterVW,

The problem I am having is that after using GDI (specifically after calling GetDC and Release DC on the DXGISurface1 associated with the D3D11 backbuffer) during a particular frame, no further rendering is possible as exceptions are raised by the rendering call. Previously I stated that the D2D1 compositing failed after a GDI call, but further investigation revealed that all rendering fails after the call to GetDC.

If ID3D11Debug.ValidateContext is called immediately prior to rendering, then that throws the same exception (see below) as Direct3DDeviceContext.DrawIndexed raises.

The exception reports "Access violation at address 642017B5 in module 'D3D11SDKLayers'dll". Read of address 0000004C.". There is no output to "Output" when the exception occurs. With the D3D11 call that throws the exception in a try...finally, D3D11InfoQueue.GetNumStoredMessages reports no messages.

The DirectX SDK is Aug 2009.

I am currently investigating why the problem does not occur in the next frame if the GetDC call is the last D3D operation in the frame rendering (as opposed to being followed by other rendering). I've tried clearing ("ClearState") and resetting the device context state after the call to GetDC but that didn't help.

There is always a chance that the problem is due to an error on my part. However it is odd (and troubling) that D3D is not diagnosing/reporting any errors.

Help would be appreciated. Strictly speaking this is no longer a D2D1 interop issue, as D2D1 is not involved in this problem, but I've raised it here because it follows on from your diagnosis advice.

JB.

Edit: Calling "Direct3DDeviceContext.OMSetRenderTargets(1,&RenderTargetView,RenderTargetDepthStencilView)" after the GetDC/ReleaseDC and before any subsequent rendering solves the problem (i.e. no exception and rendering works). GetDC is causing the render target to be set to NULL (confirmed with Direct3DDeviceContext.OMGetRenderTargets).

GetDC is giving rise to debug info message: STATE_SETTINGINFO #49: OMSETRENDERTARGETS_UNBINDDELETINGOBJECT "OMSetRenderTargets: Forcing OM Render Target slot 0 to NULL, since the resource is marked D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, but has not been acquired via IDXGIKeyedMutex::AcquireSync".

I have removed all D2D1 and I am not using the D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX flag explicitly. The only flag set for the back buffer is DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE.

There is something very unpleasant going on in D3D, because if after calling GetDC/ReleaseDC I call Direct3DDeviceContext.OMGetRenderTargets and use the returned values (i.e. a NULL render target and a valid depth buffer) in a call to Direct3DDeviceContext.OMSetRenderTargets (on simply just set the render target to NULL), the exception does not occur (though of course render does not occur either).

JB.

[Edited by - JB2009 on September 30, 2009 11:58:00 AM]

Share this post


Link to post
Share on other sites
DieterVW    724
The behavior you describe seems to be the designed behavior. The information provided in the DXGI docs for GetDC/ReleaseDC says:
----------------------------------------------------------------------------
After you use the GetDC method to retrieve a DC, you can render to the DXGI surface using GDI. The GetDC method readies the surface for GDI rendering and allows interoperation between DXGI and GDI technologies.

Keep the following in mind when using this method:

*You must create the surface using the D3D10_RESOURCE_MISC_GDI_COMPATIBLE flag for a surface or use the DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE flag for swap chains, otherwise this method will fail.
*You must release the device and call the IDXGISurface1::ReleaseDC method before issuing any new Direct3D commands.
*This method will fail if an outstanding DC has already been created by this method.
*The format for the surface or swapchain must DXGI_FORMAT_B8G8R8A8_UNORM_SRGB or DXGI_FORMAT_B8G8R8A8_UNORM
*On GetDC, the render targer in the output merger of the Direct3D pipeline is unbound from the surface. OMSetRenderTargets must be called on the device prior to Direct3D rendering after GDI rendering.
*Prior to resizing buffers you must release all outstanding DCs.
----------------------------------------------------------------------------
So, when you are using the surface with GDI, the command will unbind the D3D render target. This is not much different from if you were to bind the render target for reading in D3D -- the API would auto unbind it from the render target slot.

It'll be up to you to know when you've released the device context for the surface of the render target, then you can rebind it with the depth stencil since using GDI requires exclusive access to the surface.

Share this post


Link to post
Share on other sites
JB2009    100
DieterVW,

Many thanks for that. I confess that I missed the OMSetRenderTargets comment in the GetDC documents. I'm now clearing the render target before using GDI, and setting it again afterwards.

It does seem that when GetDC unbinds the render target, it doesn't do so properly - hence the exception during rendering. A properly unbound render target would not result in exceptions being thrown during rendering.

I'm unhappy with COM interface method throwing exceptions in situations like this - particularly access violations, which except for obvious situations such as invalid method parameters, are quite opaque. It would be better if it gave some hint as to the problem (e.g. a comment about the state of the render target). I fear you might tell me that I should have been more observant of debug info message #49! - but nevertheless I would appreciate it if you would forward the issue to the relevant person in the DirectX team. It is quite possible to write graphics libraries that are sufficiently well behaved (particularly in debug mode) that you don't have to study every word of the documentation.

Thanks again.

JB.

Share this post


Link to post
Share on other sites
DieterVW    724
Keep in mind that this exception is caused by the SDKLayers because you are using the debug device. When you use D3D directly there won't be any such exceptions, just silent failures where the pipeline doesn't execute the draw.

If nothing else, this technique prevents the developer from overlooking this issue, and infact makes it very clear where the problem lies.

It would seem, however, that the SDKLayers is not working correctly with the GetDC() method since having a NULL render target is fine.

Quote:
At this point I don't know whether we are talking weeks, months or years. (For project planning purposes, it would have been helpful if the Aug 2009 SDK included a list of what was not yet completed or working. It seems silent on the subject).

JB.


The August SDK is the completed release for the runtime, meaning no longer beta. The interop of D2D with D3D11 cannot be changed with a new SDK release since the runtime is shipped in the OS. This of course doesn't limit D3DX changes in the future.

[Edited by - DieterVW on October 2, 2009 12:47:06 PM]

Share this post


Link to post
Share on other sites
GPUShader    100
Quote:
Original post by DieterVW
The August SDK is the completed release. The interop compatibility of D2D with D3D11 cannot be changed with a new SDK release.


Is there a chance that there will be a new D3D11-backbuffer-compatible D2D beta/release sometime in the near future?

Share this post


Link to post
Share on other sites
JB2009    100
Quote:
DieterVW: The August SDK is the completed release. The interop compatibility of D2D with D3D11 cannot be changed with a new SDK release.


DieterVW,

I am bewildered by this statement. Please expand on the reasoning behind this. Surely Direct2D (though not necessarily D2D1 - maybe a later version) should be fully compatible with D3D11, as it was with D3D10.1? With D3D10.1 you can draw D2D1 content directly to the back buffer, but as we have discussed at length here, you cannot do this with D3D11. Has this functionality been removed deliberately (e.g. for a good reason, though even if it is slow, or incompatible with MSAA etc, it should still be available as an option), or did MS not get the coding finished in time, and now it is too late to fix it?

JB.

[Edited by - JB2009 on October 1, 2009 9:29:00 PM]

Share this post


Link to post
Share on other sites
JB2009    100
I've done some (preliminary) speed tests to see whether the D3D11+D2D1 workaround using a shared texture has a significant speed penalty. The result is that there is no significant speed penalty using a shared texture with D3D11 and D2D1 as opposed to using D2D1 onto the backbuffer of a D3D10.1 device, but that D2D1 appears to be very slow in both situations.

Preliminary text drawing speed test results:

-> 25 sentences of small text (25 characters each)
-> 501x501 pixel window
-> Using ID2D1RenderTarget.DrawText
-> Not using debug device(s).

D3D9: Using D3DXFONT: 0.3 ms.

D3D10.1: Using D2D1 (in hardware mode) to render directly to the back buffer: 5.0 ms.

D3D11: Using D2D1 (in hardware mode) to render to a shared texture using a D3D10.1 device, then compositing the shared texture to D3D11 (in 10.1 feature mode, to a 10.1 graphics card): 5.0 ms.

Notes:

1) Times increase for a large window/larger fonts.
2) Using ID2D1RenderTarget.DrawTextLayout is faster (approx 1.0 ms as opposed to 5.0 ms), but this is only helpful if the text is not dynamic. The 1.0 ms may be the minimum time lost whenever D2D1 is used.

-------------------------------

In these results, the D3D11 "via shared texture" approach is not slower than the D3D10.1 approach. With a larger window (e.g. 1680x1050) the D3D11 approach is slightly slower - probably due to (a) the clearing of the shared surface to transparent, and (b) the compositing.

However, although D2D1 offers a great deal of flexibility when compared to GDI or D3DXFONT, these speeds seem poor.

The 1 to 3 ms "minimum delay" when using D2D1 was, earlier in this discussion, thought to be due to the mutexes, but this may not be so. It seems that any use of D2D1 takes a few milliseconds. The same delay occues with a D3D10.1 device and no (explicit) mutexes. What is happening to use up this amount of time?

With D3D11+D2D1, I've tried not waiting for the text drawing to complete (i.e. compositing in the next frame rather than the current frame), but this does not eliminate the delay.

For one of our (main) applications, I'm currently estimating up to 10.0 ms spent drawing text (based on a mockup with the same screen size, same font sizes and similar amounts of text) if we use D2D1, as opposed to around 1 ms with D3D9. This would reduce the framerate from 25 fps to 20 fps, which takes us from (just) acceptable to not acceptable for our application. Caching (e.g. creating TextLayouts only when new text is identified) is not a complete solution in our situation, as most of the text is dynamic.

In addition, small D2D1 text is more blurred than that from GDI or D3DXFont, and I've not found any options to fix it.

JB.

[Edited by - JB2009 on October 2, 2009 4:08:16 AM]

Share this post


Link to post
Share on other sites
DieterVW    724
More information about API interop is available in blog posts on the DirectX Blog. In particular there are relavent parts in these two places:
first
second

D2D was built to use the D3D10 API, it accepts a 10 device by querying it from the resource used as a rendertarget and this is the primary design choice. Interop is available for other devices, such as D3D11 as we've discussed at length. As you've found, there is little performance difference involved between using D2D with either set of 3D APIs. So the question then becomes -- what technical problem are we solving for the future. As mentioned in the DX blog, it's great to get feedback on what problems existing going forward.

The SDK contains the headers, but the runtime is shipped in the OS.

If you can place the D2D portion of you application in another thread, would that help hide any latency that would otherwise be added to your main render loop?

Through profiling can you see where the time is spent? Is GPU time or CPU time accounting for the 5ms?

Creating TextLayouts as soon as possible is the best way to go. These have to be calculated anyway, and not cashing them is a waste. Even if a layout is discarded often becuase of dynamic content, odds are that it was still used for quite a few frames. So finding ways to create the TextLayouts whenever possible is the way to go.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this