Direct2D 和 Direct3D 互操作的方法

3
我想创建一个 Direct2D GUI,以 DLL 的形式运行,并使用注入到其中的应用程序的 Direct3D 进行渲染。 我知道可以使用 ID2D1Factory::CreateDxgiSurfaceRenderTarget 创建一个 DXGI 表面,并将其用作 D2D 渲染目标,但这需要在 Direct3D 设备上开启 D3D11_CREATE_DEVICE_BGRA_SUPPORT 标志。 问题是应用程序创建设备时没有启用此标志,因此 ID2D1Factory::CreateDxgiSurfaceRenderTarget 会失败。 我正在尝试找到另一种方法,在应用程序窗口中进行绘制(内部或外部窗口的渲染目标),并且如果该窗口处于全屏状态,也能正常工作。 迄今为止,我尝试了以下替代方案: 1. 使用 ID2D1Factory::CreateDCRenderTarget 创建 D2D 渲染目标。这个方法可以工作,但是我渲染的部分会闪烁/快速显示和隐藏。我已经在 ID2D1RenderTarget::BeginDraw 之前调用了 ID2D1DCRenderTarget::BindDC,但它仍然会闪烁,虽然比原来好一些。 2. 创建一个新窗口,始终位于所有其他窗口顶部,并在此窗口中进行 D2D 渲染,但是,如果应用程序进入全屏状态,则此窗口不会显示在屏幕上。 3. 使用启用了 D3D11_CREATE_DEVICE_BGRA_SUPPORT 标志的第二个 D3D 设备,并在窗口设备和自己之间共享 ID3D11Texture2D 资源,但我无法使其工作......没有多少关于如何做这件事的示例。我的想法是创建第二个设备,在该设备上使用 D2D 进行绘制,然后同步 2 个 D3D 设备-我遵循了 这个示例(使用 direct11)。 4. 创建一个 D2D 设备并共享 d2d 设备的数据与 d3d 设备, 但是当调用 ID2D1Factory1::CreateDevice 创建设备时它会失败,因为创建 D3D 设备时未启用 D3D11_CREATE_DEVICE_BGRA_SUPPORT标志。我从 这个示例 开始。

我听说过硬件叠加,但它只适用于某些显卡,我认为我会遇到问题。参考链接:https://learn.microsoft.com/el-gr/windows/win32/medfound/hardware-overlay-support

目前我已经陷入了僵局,不知道该怎么办。有没有人有什么想法可以帮助我?

也许有一种方法可以在屏幕上绘图并工作,即使窗口是全屏状态下的?

1个回答

2
#3是正确的答案。以下是一些提示。
不要使用键控互斥体。不要使用NT句柄。你只需要一个标志D3D11_RESOURCE_MISC_SHARED
为了正确地同步跨设备访问共享纹理,使用查询。具体来说,你需要一个类型为D3D11_QUERY_EVENT的查询。工作流程应该如下所示。
1.在一个设备上创建一个共享纹理,在另一个设备上打开它。无论它在哪里创建,在哪里导入都没有关系。不要忘记D3D11_BIND_RENDER_TARGET标志。还要创建一个查询。
2.使用共享纹理的CreateDxgiSurfaceRenderTarget创建D2D设备,使用D2D和/或DirectWrite将覆盖层呈现到共享纹理中。
3.在用于D2D渲染的BGRA标志的立即D3D设备上下文中,调用ID3D11DeviceContext.End一次,传递查询。然后等待ID3D11DeviceContext.GetData返回S_OK。如果你关心电力/热量,使用Sleep(1),如果你优先考虑延迟,则使用_mm_pause()指令进行忙等待。
4.一旦ID3D11DeviceContext.GetData为该查询返回S_OK,GPU就完成了你的2D场景渲染。现在你可以在另一个设备上使用该纹理来组合成3D场景。
将你的2D内容合成到渲染目标中的方式取决于你想要如何绘制你的2D内容。
如果是一个小的不透明的四边形,你可能可以CopySubresourceRegion到渲染目标纹理中。
或者,如果你的2D内容有透明背景,你需要使用顶点+像素着色器来渲染一个用你共享的纹理贴图的四边形(4个顶点)。顺便说一下,你不一定需要一个顶点/索引缓冲区,有一个众所周知的技巧可以不用它。不要忘记混合状态(你可能想要alpha混合),深度/模板状态(当渲染那个四边形时,你可能想要禁用深度测试),还有共享纹理的D3D11_BIND_SHADER_RESOURCE标志。
附言:还有另一种方法。确保你的代码在该进程创建其Direct3D设备之前运行。然后使用类似于minhook的东西来拦截对D3D11.dll::D3D11CreateDeviceAndSwapChain的调用,在拦截的函数中设置你需要的BGRA位,然后调用原始函数。稍微不太可靠,因为有多种创建D3D设备的方法,但更容易实现,速度更快,占用的内存更少。

@modZz 第一种方法仅使用支持的API,没有钩子。请查看更新,希望工作流程现在更好了。 - Soonts
我不会太担心检测问题。像 D3D11CreateDeviceAndSwapChain 这样的 Windows 实现的 API 函数挂钩并不是很容易被检测到,因为微软正在使用 Windows 更新替换实现。在现代 Windows 中,微软甚至可以将实现移动到 DLL 中,而不会破坏使用 API 的程序的二进制兼容性。 - Soonts
1
@modZz 我建议不要使用NT句柄,即D3D11_RESOURCE_MISC_SHARED_NTHANDLE标志。微软公司建议相反,但我记得使用NT句柄时会有兼容性问题。 - Soonts
@modZz 看,调试信息告诉你出了什么问题。也许你正在尝试共享一个BGRA纹理。如果是这种情况,请将像素格式更改为RGBA。或者你可能正在使用错误的D3D设备或纹理。如果你已经在主设备上创建了共享纹理并导入到自己的BGRA纹理中,则必须使用导入的纹理对象来初始化D2D,或者翻转共享方向(即在BGRA设备上创建纹理并导入到主设备上)。 - Soonts
@modZz 作为一个快速的解决方法来测试事情,可以在游戏设置中禁用MSAA。适当的解决方法是仍然渲染纹理四边形。 - Soonts
显示剩余16条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接