Direct2D 和 Direct3D 互操作性的方法

Posted

技术标签:

【中文标题】Direct2D 和 Direct3D 互操作性的方法【英文标题】:ways for Direct2D and Direct3D Interoperability 【发布时间】:2021-12-03 22:35:04 【问题描述】:

我想制作一个 Direct2D GUI,它将在 DLL 上运行,并使用我注入其中的应用程序的 Direct3D 进行渲染。

我知道我可以简单地使用 ID2D1Factory::CreateDxgiSurfaceRenderTarget 来制作 DXGI 表面并将其用作 d2d 渲染目标,但这需要在 Direct3D 的设备上启用标志 D3D11_CREATE_DEVICE_BGRA_SUPPORT

问题是应用程序在未启用此标志的情况下创建其设备,因此ID2D1Factory::CreateDxgiSurfaceRenderTarget 失败。

我正在尝试找到另一种在应用程序窗口(外部或窗口的渲染目标内部)上绘制的方法,如果该窗口处于全屏状态,该方法也可以使用。

到目前为止,我尝试了这些替代方法:

    使用ID2D1Factory::CreateDCRenderTarget 创建一个 d2d 渲染目标。这行得通,但我渲染的部分是闪烁/闪烁(在循环中非常快速地显示和隐藏)。我在ID2D1RenderTarget::BeginDraw之前也调用了ID2D1DCRenderTarget::BindDC,但它只是闪烁但有点少,所以我仍然遇到同样的问题。

    创建一个新窗口,该窗口将始终位于所有其他窗口的顶部并使用 d2d 进行渲染,但如果应用程序进入全屏状态,则此窗口不会显示在屏幕上。

    创建第二个启用D3D11_CREATE_DEVICE_BGRA_SUPPORT 标志的D3D 设备,并在窗口设备和我自己的设备之间共享ID3D11Texture2D 资源,但我无法让它工作......没有很多关于如何做到这一点的例子。想法是创建第二个设备,在该设备上使用 d2d 绘图,然后同步两个 D3D 设备——我关注了this example(使用 direct11)。

    创建一个D2D设备,并与d3d设备共享d2d设备的数据;但是,当我调用 ID2D1Factory1::CreateDevice 创建设备时,它会失败,因为创建 D3D 设备时没有启用 D3D11_CREATE_DEVICE_BGRA_SUPPORT 标志。我从this example开始。

我听说过硬件覆盖,但它只适用于某些显卡,我想我会遇到这个https://docs.microsoft.com/el-gr/windows/win32/medfound/hardware-overlay-support 的问题。

我目前处于死胡同;我不知道该怎么办。有没有人有任何想法可以帮助我?

即使窗口处于全屏状态,是否有任何方法可以在屏幕上绘图和工作?

【问题讨论】:

【参考方案1】:

#3 是正确的。这里有一些提示。

不要使用键控互斥锁。不要使用 NT 句柄。您唯一需要的标志是D3D11_RESOURCE_MISC_SHARED

要跨设备正确同步对共享纹理的访问,请使用查询。具体来说,您需要D3D11_QUERY_EVENT 类型的查询。工作流程应如下所示。

    在一台设备上创建共享纹理,在另一台设备上打开。它的创建位置和导入位置无关紧要。不要忘记D3D11_BIND_RENDER_TARGET 标志。同时创建一个查询。

    使用共享纹理的 CreateDxgiSurfaceRenderTarget 创建 D2D 设备,使用 D2D 和/或 DirectWrite 将叠加层渲染到共享纹理中。

    在带有用于 D2D 渲染的 BGRA 标志的直接 D3D 设备上下文中,调用一次ID3D11DeviceContext.End,并传递查询。然后等待ID3D11DeviceContext.GetData 返回 S_OK。如果您关心电力/热能使用Sleep(1),或者如果您优先考虑延迟,请使用_mm_pause() 指令忙等待。

    一旦ID3D11DeviceContext.GetData 为该查询返回了 S_OK,GPU 就完成了您的 2D 场景的渲染。您现在可以在其他设备上使用该纹理合成 3D 场景。

将 2D 内容组合到渲染目标的方式取决于您希望如何绘制 2D 内容。

如果那是一个小的不透明四边形,您可能可以CopySubresourceRegion 进入渲染目标纹理。

或者,如果您的 2D 内容具有透明背景,您需要一个顶点+像素着色器来渲染一个使用共享纹理纹理的四边形(4 个顶点)。顺便说一句,你不一定需要一个顶点/索引缓冲区,有一个 well-known trick 可以不用。不要忘记混合状态(您可能需要 Alpha 混合)、深度/模板状态(您可能希望在渲染该四边形时禁用深度测试)以及共享纹理的 D3D11_BIND_SHADER_RESOURCE 标志。

附:还有另一种方式。确保您的代码在该进程创建其 Direct3D 设备之前在该进程中运行。然后使用minhook之类的东西来拦截对D3D11.dll::D3D11CreateDeviceAndSwapChain的调用,在拦截的函数中设置你需要的BGRA位,然后调用原始函数。可靠性稍差,因为有多种方法可以创建 D3D 设备,但更容易实现,运行速度更快,使用的内存更少。

【讨论】:

我尽量避免使用钩子,因为我害怕反作弊检测,我想使用 minhook 来钩住该函数(或在编译的应用程序中更改该参数值),但两者都很容易被检测到.共享纹理的第一种方式需要任何钩子(例如:在应用程序渲染主图形的地方渲染它)? @modZz 第一种方法只使用支持的 API,没有钩子。查看更新,希望现在的工作流程更好。 我不会太担心检测。像D3D11CreateDeviceAndSwapChain 这样的 Windows 实现的 API 函数挂钩并不容易检测,因为 Microsoft 正在用 Windows 更新替换实现。在现代 Windows 中,微软甚至可以跨 DLL 移动实现,而不会破坏使用 API 的程序的二进制兼容性。 感谢您的回答,我将尝试使用共享纹理创建它,因为有时我想在应用程序运行时注入和取消注入 dll。该钩子要求在应用程序启动时始终加载 DLL @modZz 我建议不要使用 NT 句柄,即D3D11_RESOURCE_MISC_SHARED_NTHANDLE 标志。 Microsoft 建议相反,但我记得 NT 句柄的兼容性问题。

以上是关于Direct2D 和 Direct3D 互操作性的方法的主要内容,如果未能解决你的问题,请参考以下文章

Direct2D教程Direct2D已经来了,谁是GDI的终结者?

WinRT 和软件 ClearType 字体渲染

使用命名空间代替类

使用 Direct 3D (C++) 的 DIrectX 11 2D 游戏引擎

Direct2D教程渲染位图

《COM本质论》读书笔记