Multithreading 如何重新编写现有的基于Direct2D的库,使其能够同时从多个线程访问函数?

Multithreading 如何重新编写现有的基于Direct2D的库,使其能够同时从多个线程访问函数?,multithreading,direct2d,directx-10,direct3d10,Multithreading,Direct2d,Directx 10,Direct3d10,我正在使用一个现有的图形库,其中一个实现使用Direct2D。(它也可以使用GDI+、Quartz等)它有位图对象、笔、画笔等,所有这些都作为Direct2D对象的包装代码实现。该库是封闭源代码的,不幸的是,我可能无法在这里发布它的大部分内容 我发现,当使用多个线程时——一个线程生产者,创建位图并绘制到它们,一个线程消费者,在屏幕上绘制它们——有时生产者线程生成的位图是完全空白的:图像数据(内部为纹理)都是零。(位图是纹理周围的一个包装对象,但它代表的是一个类似于图形的位图,你可能会想到GDI+

我正在使用一个现有的图形库,其中一个实现使用Direct2D。(它也可以使用GDI+、Quartz等)它有位图对象、笔、画笔等,所有这些都作为Direct2D对象的包装代码实现。该库是封闭源代码的,不幸的是,我可能无法在这里发布它的大部分内容

我发现,当使用多个线程时——一个线程生产者,创建位图并绘制到它们,一个线程消费者,在屏幕上绘制它们——有时生产者线程生成的位图是完全空白的:图像数据(内部为纹理)都是零。(位图是纹理周围的一个包装对象,但它代表的是一个类似于图形的位图,你可能会想到GDI+位图对象。我相信你熟悉一般的范例。)请注意,它从不崩溃,只是有时会无声地失败。典型的症状是绘制位图数据,调用
Unmap
从缓冲区复制回纹理,并在稍后尝试绘制纹理时发现纹理为空白

我认为正在发生的是:

  • 要直接写入像素数据,正如我的producer线程所做的那样,库提供了
    Map
    Unmap
    函数来直接访问像素数据。有一个DirectX10设备,一个“共享设备”,用于调用从内部纹理复制到缓冲区,然后可以自由写入,然后以另一种方式取消映射副本。我想有时候这会悄悄地失败
  • 这种共享设备的方法在整个库中使用:有一个共享渲染目标(
    ID2D1RenderTarget
    )用于创建共享位图(
    ID2D1Bitmap
    ),等等。这种方法似乎是图形/位图对象有自己的纹理,当然,但对它们的所有操作都是用一个设备完成的,一个渲染目标等
  • 当多个线程同时使用“位图”时,多个操作可能会失败:
    copyrource
    (如上所述),返回
    void
    not
    HRESULT
    ,有时会失败;也默默地失败了
因此,我想解决这个问题,并侵入库中足够的线程安全性,我可以可靠地实现生产者-消费者线程系统。我如何做到这一点

我已经读了很多关于从多个线程使用Direct2D的正确方法的书

  • 所有工厂都已经创建了

  • 我试着用这个。然而,这似乎只适用于Windows7及以上版本:我需要使用DirectX10级别的Vista+API

  • 我还尝试了使用用于同步的。我在这方面遇到了一些问题:

    我不确定我应该在哪里同步,或者围绕什么同步。如果是细粒度的,例如大约
    Map
    Unmap
    调用,或者MSDN指示的大约
    Present
    ,则同步无效:上述方法调用仍然失败。如果它是粗粒度的,则在调用时输入锁,在所有绘图中保持锁,并在调用
    ID2D1RenderTarget::EndDraw
    ID2D1RenderTarget::Flush
    和/或
    IDXGISwapChain::Present
    后离开锁,它似乎工作得非常出色。。。直到几秒钟后,我进入了死锁状态。MSDN对此进行了描述

    注意不要让泵线程等待消息出现在屏幕上 使用全屏交换链时渲染线程。例如, 调用IDXGISwapChain1::Present1(从渲染线程)可能会导致 要在消息泵线程上等待的渲染线程。当一个模式 发生更改时,如果Present1调用 ::SetWindowPos()或::setWindowsStyle()以及以下任一方法 调用::SendMessage()。在这种情况下,如果消息pump thread 保护它的关键部分,或者如果渲染线程被阻塞, 然后这两个线程将死锁。-

    …但没有关于如何避免的指导。我的死锁似乎是由于两个线程都试图访问图形,例如同时调用
    BeginDraw
    。似乎还有第二个锁在运行,因为如果只有一个锁,那么就不会出现死锁

  • 我曾尝试保留“共享”设备、渲染目标等的每个线程实例。也就是说,每种类型对象的每个线程都有一个实例,然后在该线程中运行的所有代码都会使用该实例。这很好,除了纹理:访问在另一个线程上下文(由另一个设备)中创建的纹理似乎根本不起作用。在编码这种技术时,我经常遇到方法失败的问题-最常见的是
    CreateSharedBitmap
    ,它在
    dDerr\u UNSUPPORTED\u操作中失败
    ,其中纹理来自另一个线程(因此,另一个设备)。这是关键位,因为位图对象(包装纹理)在一个线程中创建的需要能够在另一个线程中绘制。根据编码方式的不同,线程和设备、渲染目标等之间可能存在也可能不存在1:1的关系

    如果我能解决这个问题,我认为我编写的其他代码足够好,可以实现我需要的功能。可能吗?如何可能要在
    ID3D10Device1
    s之间交叉
    ID3D10Texture2D
    s?是否有必要跨线程使用单独的设备

总而言之,我有点不知所措,正在向熟悉Direct3D 10和Direct2D的人寻求关于最佳方法的建议。我认为有两种可行的可能性:

  • 找出锁。关于谷歌,也没有太多关于避免MSDN死锁的建议

  • 继续使用每个线程都有自己的设备、共享渲染目标等的机制