Ideally I'd like to have multiple worker threads to be able to render to off-screen render targets and then 'transfer' the rendered content to the on-screen target. With hwnd render targets this does not seem to be a problem (msdn has an example of it).
I'm not quite sure how to do it when the on-screen render target is based on DXGI swap chain. As far as I know, I can only have one swap chain per window. Therefore I can only have a single render target based on the swap chain. This implies that on-screen rendering can only be done through that single render target.
If my above assumptions are correct, what is the best way to handle the multi-threaded rendering? Do I need to serialize access to the on-screen target? Should worker threads share a single multi-threaded d2d factory? Can on-screen target's BeginDraw/EndDraw/Present be executed on worker threads (i.e. threads that did not create the on-screen target) if a proper locking mechanism is in place?
I'd appreciate any suggestions.
Thanks.
I'm tackling this same issue right now! According to my reading on MSDN, the best way is:
- Use a multithreaded factory - this lets you share resources (see also link with the quote below).
- Be careful with some deadlock situations (details below.)
- You probably need to use ID2D1MultiThread to lock while drawing using Direct3D.
I have not yet got a reliably working multithreaded set of Direct2D functions, even following this, so this is all I know so far - I don't yet know about the other caveats etc that are sure to exist.
Some useful key bits:
You can create a multithreaded Direct2D factory instance. You can use
and share a multithreaded factory and all it's resources from more
than one thread, but accesses to those resources (via Direct2D calls)
are serialized by Direct2D, so no access conflicts occur. If your app
calls only Direct2D APIs, such protection is automatically done by
Direct2D in a granular level with minimum overhead.
ID2D1Factory* m_D2DFactory;
// Create a Direct2D factory.
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_MULTI_THREADED,
&m_D2DFactory
);
There is also this very important warning:
Multithread Considerations
When you use DXGI in an application with
multiple threads, you need to be careful to avoid creating a deadlock,
where two different threads are waiting on each other to complete.
There are two situations where this can occur.
- The rendering thread is
not the message-pump thread.
- The thread executing a DXGI API is not
the same thread that created the window.
Be careful that you never
have the message-pump thread wait on the render thread when you use
full-screen swap chains. For instance, calling
IDXGISwapChain1::Present1 (from the render thread) may cause the
render thread to wait on the message-pump thread. When a mode change
occurs, this scenario is possible if Present1 calls ::SetWindowPos()
or ::SetWindowStyle() and either of these methods call
::SendMessage(). In this scenario, if the message-pump thread has a
critical section guarding it or if the render thread is blocked, then
the two threads will deadlock.
For offscreen render targets, you can create separate D2DFactories and attach it to respective offscreen render targets created using CreateWicBitmapRenderTarget or CreateDxgiSurfaceRenderTarget. While transferring it to the onscreen target, you will have to wait for all the threads to finish and then transfer each off-screen RT to on-screen RT one by one.