高效的Direct2D多线程 [英] Efficient Direct2D multithreading
问题描述
我正在为Windows应用商店编写一个电子书阅读器应用程序.我正在使用Direct2D + DXGI交换链在屏幕上呈现书籍页面.
I'm writing a ebook reader app for Windows Store. I'm using Direct2D + DXGI swap chains to render book pages on screen.
我的书内容有时非常复杂(几何,位图,蒙版等),因此呈现它可能需要100毫秒的时间.因此,我尝试在单独的线程中对位图进行屏幕外渲染,然后仅在主线程中显示此位图.
My book content sometimes is quite complex (geometry, bitmaps, masks, etc), so it can take up to 100 ms to render it. So I'm trying to do an off-screen rendering to a bitmap in a separate thread, and then just show this bitmap in main thread.
但是,我不知道如何有效地做到这一点.
However, I can't figure how to do it efficiently.
到目前为止,我已经尝试了两种方法:
So far I've tried two approaches:
-
使用带有D2D1_FACTORY_TYPE_MULTI_THREADED标志的单个
ID2D1Factory
,创建ID2D1BitmapRenderTarget
并在后台线程中使用它进行屏幕外渲染. (这另外需要IDXGISwapChain::Present
操作上的ID2D1Multithread::Enter/Leave
).问题是,后台线程中的ID2D1RenderTarget::EndDraw
操作有时需要长达100ms的时间,并且由于内部Direct2D锁定,在此期间主线程渲染被阻止.
Use a single
ID2D1Factory
with D2D1_FACTORY_TYPE_MULTI_THREADED flag, createID2D1BitmapRenderTarget
and use it in background thread for off-screen rendering. (This additionally requiresID2D1Multithread::Enter/Leave
onIDXGISwapChain::Present
operations). Problem is,ID2D1RenderTarget::EndDraw
operation in background thread sometimes take up to 100ms, and main thread rendering is blocked for this period due to internal Direct2D locking.
在后台线程中使用单独的ID2D1Factory
(如 http://www.sdknews.com/ios/using-direct2d-for-server-side-rendering ),然后关闭内部Direct2D同步.在这种情况下,两个线程之间没有交叉锁定.不幸的是,在这种情况下,我不能直接在主ID2D1Factory
中使用生成的位图,因为它属于另一个工厂.我必须将位图数据移动到CPU内存,然后将其复制到主ID2D1Factory
的GPU内存中.此操作还会带来严重的延迟(我相信这是由于访问大量内存,但我不确定).
Use a separate ID2D1Factory
in background thread (as described in http://www.sdknews.com/ios/using-direct2d-for-server-side-rendering) and turn off internal Direct2D synchronization. There is no cross-locking betwen two threads in this case. Unfortunately, in this case I can't use resulting bitmap in main ID2D1Factory
directly, because it belongs to a different factory. I have to move bitmap data to CPU memory, then copy it into GPU memory of the main ID2D1Factory
. This operation also introduce significant lags (I believe it to be due to large memory accesses, but I'm not sure).
有没有办法有效地做到这一点?
Is there a way to do this efficiently?
P.S.此处列出了Acer Switch 10平板电脑的所有时间.在常规的Core i7 PC上,两种方法都可以正常工作,而不会出现明显的延迟.
P.S. All the timing here are given for Acer Switch 10 tablet. On regular Core i7 PC both approaches work without any visible lag.
推荐答案
好的,我已经找到了解决方案.
Ok, I've found a solution.
基本上,我所需要做的就是修改方法2,以在两个DirectX工厂集之间使用DXGI资源共享.我将跳过所有详细信息(可以在以下位置找到它们: http://xboxforums.create.msdn.com/forums/t/66208.aspx ),但基本步骤是:
Basically, all I needed is to modify approach 2 to use DXGI resource sharing between two DirectX factory sets. I'll skip all the gory details (they can be found here: http://xboxforums.create.msdn.com/forums/t/66208.aspx), but basic steps are:
- 创建两组DirectX资源:主要(用于屏幕渲染)和次要(用于屏幕外渲染).
- 使用主资源集中的
ID3D11Device2
,通过CreateTexture2D
,D3D11_BIND_RENDER_TARGET
,D3D11_BIND_SHADER_RESOURCE
,D3D11_RESOURCE_MISC_SHARED_NTHANDLE
和D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
标志创建D3D 2D纹理. - 通过将其转换为
IDXGIResource1
并使用XGI_SHARED_RESOURCE_READ
和DXGI_SHARED_RESOURCE_WRITE
从其调用CreateSharedHandle
来从中获取共享句柄. - 通过调用
ID3D11Device2::OpenSharedResource1
在后台线程中设置的辅助资源中打开此共享纹理. - 获取此纹理(
IDXGIKeyedMutex::AcquireSync
)的键控互斥锁,从中创建渲染目标(ID2D1Factory2::CreateDxgiSurfaceRenderTarget
),在其上绘制并释放互斥锁(IDXGIKeyedMutex::ReleaseSync
). - 在主线程上的主资源集中,获取互斥锁并从步骤2中创建的纹理创建共享位图,绘制该位图,然后释放互斥锁.
- Create two sets of DirectX resources: main (which will be used to onscreen rendering), and secondary (for offscreen rendering).
- Using
ID3D11Device2
from main resource set, create D3D 2D texture byCreateTexture2D
D3D11_BIND_RENDER_TARGET
,D3D11_BIND_SHADER_RESOURCE
,D3D11_RESOURCE_MISC_SHARED_NTHANDLE
andD3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX
flags. - Get shared handle from it by casting it to
IDXGIResource1
and callingCreateSharedHandle
from it withXGI_SHARED_RESOURCE_READ
andDXGI_SHARED_RESOURCE_WRITE
. - Open this shared texture in secondary resource set in background thread by calling
ID3D11Device2::OpenSharedResource1
. - Acquire keyed mutex of this texture (
IDXGIKeyedMutex::AcquireSync
), create render target from it (ID2D1Factory2::CreateDxgiSurfaceRenderTarget
), draw on it and release mutex (IDXGIKeyedMutex::ReleaseSync
). - On the main thread, in the main resource set, acquire mutex and create shared bitmap from texture created in step 2, draw this bitmap, then release mutex.
请注意,互斥锁必须是必需的.不这样做会导致一些神秘的DirectX调试错误消息,并导致错误的操作甚至崩溃.
Note that mutex locking stuff is necessary. Not doing it results in some cryptic DirectX debug error messages, and erroneous operation or even crashing.
这篇关于高效的Direct2D多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!