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的终结者?