高效的Direct2D多线程

时间:2015-10-15 08:32:30

标签: multithreading direct2d

我正在为Windows应用商店编写电子书阅读器应用程序。我使用Direct2D + DXGI交换链在屏幕上呈现书页。

我的书内容有时非常复杂(几何,位图,蒙版等),因此渲染它可能需要100毫秒。所以我试图在单独的线程中对位图进行离屏渲染,然后在主线程中显示这个位图。

但是,我无法确定如何有效地做到这一点。

到目前为止,我尝试了两种方法:

  1. 使用带有D2D1_FACTORY_TYPE_MULTI_THREADED标志的单个ID2D1Factory,创建ID2D1BitmapRenderTarget并在后台线程中使用它进行屏幕外渲染。 (这需要在ID2D1Multithread::Enter/Leave操作上IDXGISwapChain::Present。问题是,后台线程中的ID2D1RenderTarget::EndDraw操作有时需要长达100毫秒,并且由于内部Direct2D锁定,主线程渲染在此期间被阻止。

  2. 在后台线程中使用单独的ID2D1Factory(如http://www.sdknews.com/ios/using-direct2d-for-server-side-rendering中所述)并关闭内部Direct2D同步。在这种情况下,两个线程之间没有交叉锁定。不幸的是,在这种情况下,我无法直接在主ID2D1Factory中使用结果位图,因为它属于不同的工厂。我必须将位图数据移动到CPU内存,然后将其复制到主ID2D1Factory的GPU内存中。这个操作也引入了显着的滞后(我认为这是由于大量的内存访问,但我不确定)。

  3. 有没有办法有效地做到这一点?

    P.S。这里的所有时间都是针对Acer Switch 10平板电脑提供的。在常规Core i7 PC上,两种方法都可以在没有任何明显滞后的情况下工作。

2 个答案:

答案 0 :(得分:6)

好的,我找到了解决方案。

基本上,我需要的是修改方法2以在两个DirectX工厂集之间使用DXGI资源共享。我将跳过所有血腥的细节(可以在这里找到:http://xboxforums.create.msdn.com/forums/t/66208.aspx),但基本步骤是:

  1. 创建两组DirectX资源:main(用于屏幕渲染)和辅助(用于屏幕外渲染)。
  2. 使用主资源集中的ID3D11Device2,按CreateTexture2D D3D11_BIND_RENDER_TARGETD3D11_BIND_SHADER_RESOURCED3D11_RESOURCE_MISC_SHARED_NTHANDLED3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX标记创建D3D 2D纹理。< / LI>
  3. 通过将其转换为IDXGIResource1并使用CreateSharedHandleXGI_SHARED_RESOURCE_READDXGI_SHARED_RESOURCE_WRITE调用ID3D11Device2::OpenSharedResource1来获取共享句柄。
  4. 通过调用IDXGIKeyedMutex::AcquireSync
  5. 在后台线程中的辅助资源集中打开此共享纹理
  6. 获取此纹理的关键互斥锁(ID2D1Factory2::CreateDxgiSurfaceRenderTarget),从中创建渲染目标(IDXGIKeyedMutex::ReleaseSync),在其上绘制并释放互斥锁(RTP: missed packet)。
  7. 在主线程上,在主资源集中,获取互斥锁并从步骤2中创建的纹理创建共享位图,绘制此位图,然后释放互斥锁。
  8. 请注意,互斥锁定是必要的。不这样做会导致一些神秘的DirectX调试错误消息,以及错误的操作甚至崩溃。

答案 1 :(得分:1)

tl; dr:在软件模式下渲染到后台线程上的位图。从位图绘制以在硬件模式下在UI线程上渲染目标。

到目前为止,我能找到的最佳方法是使用后台线程与软件呈现(IWICImagingFactory::CreateBitmapID2D1Factory::CreateWicBitmapRenderTarget),然后将其复制到硬件位图通过ID2D1RenderTarget::CreateBitmapFromWicBitmap返回硬件渲染目标的线程。然后使用ID2D1RenderTarget::DrawBitmap进行blit。

这就是paint.net 4.0选择渲染的方式。当您使用套索工具绘制选区时,它将使用后台线程异步绘制选择大纲(UI线程不会等待此操作完成)。由于笔触样式和动画,您最终可能会得到一个非常复杂的多边形。我渲染了4次,其中每个动画帧的虚线描边样式的偏移量略有不同。

显然,这种渲染可能需要一段时间,因为多边形变得更加复杂(也就是说,如果你继续涂鸦一段时间)。当你使用移动选择工具允许你进行转换(旋转,平移,缩放)时,我还有一些其他的特殊优化:如果后台线程尚未使用新变换重新渲染当前多边形,那么我将应用新变换渲染旧位图(使用当前多边形和旧变换)。当背景线程赶上时,选择轮廓可能会扭曲(缩放)或剪裁(在可视区域之外翻译),但是为60fps的响应速度付出的代价很小。此优化非常有效,因为您无法同时修改选区的多边形变换。