Effective Direct2D Multithreading

I am writing an e-book reader application for the Windows Store. I use Direct2D + DXGI swap to display book pages on screen.

The contents of my book are sometimes quite complex (geometry, bitmaps, masks, etc.), so it may take up to 100 ms to render it. So I'm trying to render off-screen to a bitmap in a separate stream, and then just show that bitmap in the main stream.

However, I cannot figure out how to do this effectively.

So far I have tried two approaches:

  • Use one ID2D1Factory with the D2D1_FACTORY_TYPE_MULTI_THREADED flag, create an ID2D1BitmapRenderTarget and use it in the background stream to render off-screen. (This additionally requires ID2D1Multithread::Enter/Leave for IDXGISwapChain::Present operations). The problem is that the operation ID2D1RenderTarget::EndDraw in the background thread sometimes takes up to 100 ms, and the rendering of the main thread is blocked during this period due to internal blocking of Direct2D.

  • Use a separate ID2D1Factory in the background thread (as described at http://www.sdknews.com/ios/using-direct2d-for-server-side-rendering ) and disable Direct2D internal synchronization. In this case, there is no cross-lock between the two threads. Unfortunately, in this case, I cannot use the resulting raster map basically ID2D1Factory directly, because it belongs to another factory. I need to move the raster data to the processor memory, and then copy it to the GPU memory of the main ID2D1Factory . This operation also leads to significant delays (I believe this is due to more memory access, but I'm not sure).

Is there any way to do this efficiently?

PS All terms are given here for the Acer Switch 10 tablet. On a regular Core i7 PC, both approaches work without any noticeable lag.

+5
source share
2 answers

Ok, I found a solution.

Basically, I just need to change approach 2 to use DXGI sharing between two DirectX factory sets. I will skip all the details of gory (you can find them here: http://xboxforums.create.msdn.com/forums/t/66208.aspx ), but the main steps are:

  • Create two sets of DirectX resources: main (which will be used for rendering on the screen) and secondary (for rendering on the screen).
  • Using ID3D11Device2 from the main resource set, create a 2D D3D texture using the CreateTexture2D flags D3D11_BIND_RENDER_TARGET , D3D11_BIND_SHADER_RESOURCE , D3D11_RESOURCE_MISC_SHARED_NTHANDLE
  • Get the general descriptor from it by translating it to IDXGIResource1 and calling CreateSharedHandle from it using XGI_SHARED_RESOURCE_READ and DXGI_SHARED_RESOURCE_WRITE .
  • Open this shared texture in the secondary resource specified in the background thread by calling ID3D11Device2::OpenSharedResource1 .
  • Get the mutex key of this texture ( IDXGIKeyedMutex::AcquireSync ), create a rendering object from it ( ID2D1Factory2::CreateDxgiSurfaceRenderTarget ), draw it and release the mutex ( IDXGIKeyedMutex::ReleaseSync ).
  • In the main thread, in the main resource set, get a mutex and create a common bitmap from the texture created in step 2, draw this bitmap, then release the mutex.

Please note that you must lock the mutex lock. Not doing this leads to some cryptic messages about DirectX debugging errors, as well as erroneous work or even crashes.

+4
source

tl; dr: Render to bitmaps in the background thread in program mode. Draw from bitmaps to display the target in the UI thread in hardware mode.

The best approach that I have been able to find so far is to use background streams with software rendering ( IWICImagingFactory::CreateBitmap and ID2D1Factory::CreateWicBitmapRenderTarget ), and then copy it to the equipment bitmap in the stream using the target equipment rendering through ID2D1RenderTarget::CreateBitmapFromWicBitmap . And then blit using ID2D1RenderTarget::DrawBitmap .

Since paint.net 4.0 does rendering highlighting. When you draw a selection using the lasso tool, it will use a background thread to asynchronously draw a selection outline (the user interface thread does not wait for this to finish). You can get a very complex polygon due to the stroke style and animation. I do it 4 times, where each animation frame has a slightly different offset for the dash bar style.

Obviously, this rendering may take some time, as the polygon becomes more complex (that is, if you keep writing for a while). I have several other special optimizations when you use the Move Selection tool, which allows you to do transforms (rotate, translate, scale): if the background thread has not yet re-mapped the current polygon with the new transformation, then I will display the old bitmap (with current polygon and old transform) with the new transform applied. The allocation scheme may be distorted (scaled) or cropped (moved outside the visible region), while the background stream is gaining momentum, but this is a small price to pay for 60fps responsiveness. This optimization works very well because you cannot change the polygon and transform the selection at the same time.

0
source

Source: https://habr.com/ru/post/1233763/


All Articles