What are the best practices for multithreading wit

2019-04-10 13:27发布

问题:

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.

回答1:

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
);
  • Source.

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.

  • Source.


回答2:

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.



标签: direct2d