14

数周以来,我一直在为 C# 天搜索和测试不同类型的渲染库。到目前为止,我还没有找到一个在多窗口渲染设置上运行良好的库。要求是能够在 12 个以上的显示器设置(财务图表)上运行程序,而不会在快速计算机上出现延迟。每个窗口需要每秒更新多次。虽然这样做 CPU 需要执行大量密集且时间紧迫的任务,因此必须将一些负担转移到 GPU 上。这就是硬件渲染介入的地方,换句话说就是 DirectX 或 OpenGL。

我已经尝试过使用 Windows 窗体的 GDI+,并认为它对于我的需求来说太慢了。我已经通过 OpenTK(在 Windows 窗体控件上)尝试了 OpenGL,这似乎相当快(我仍然要在其上运行一些测试),但很难正常工作(很难找到/编写好的文本渲染库)。最近我通过 SharpDX 在 Windows 窗体上尝试了 DirectX9、DirectX10 和 Direct2D。我为每个窗口尝试了一个单独的设备,并尝试了一个设备/多个交换链方法。所有这些都导致在多个窗口上的性能非常差。例如,如果我将目标 FPS 设置为 20 并在不同的显示器上打开 4 个全屏窗口,整个操作系统就会开始严重滞后。渲染只是将屏幕清除为黑色,不渲染图元。这个测试的 CPU 使用率约为 0%,GPU 使用率约为 10%,我不知道 不明白这里的瓶颈是什么?我的开发计算机非常快,i7 2700k,AMD HD7900,16GB 内存,所以测试肯定应该在这台上运行。

相比之下,我在 C++/Win32 API 一个设备/多个交换链上做了一些 DirectX9 测试,我可以打开遍布 4 显示器工作区的 100 个窗口(3D 茶壶在上面旋转),并且仍然拥有完全负责的操作系统(fps 是当然,渲染窗口非常糟糕地下降到大约 5,这是我期望运行 100 个同时渲染的结果)。

有谁知道在 C# 上进行多窗口渲染的任何好方法,还是我被迫用 C++ 重写我的程序以获得该性能(主要痛苦)?我想在我走 C++ 路线之前,我要再给 OpenGL 一个机会。我会在这里报告任何发现。

测试方法供参考:

对于 C# DirectX 单设备多交换链测试,我使用了这个出色答案中的方法: Display Different images per monitor directX 10

Direct3D10 版本:

我像这样创建了 d3d10device 和 DXGIFactory:

D3DDev = new SharpDX.Direct3D10.Device(SharpDX.Direct3D10.DriverType.Hardware,
            SharpDX.Direct3D10.DeviceCreationFlags.None);
DXGIFac = new SharpDX.DXGI.Factory();

然后像这样初始化渲染窗口:

var scd = new SwapChainDescription();
scd.BufferCount = 1;
scd.ModeDescription = new ModeDescription(control.Width, control.Height,
      new Rational(60, 1), Format.R8G8B8A8_UNorm);
scd.IsWindowed = true;
scd.OutputHandle = control.Handle;
scd.SampleDescription = new SampleDescription(1, 0);
scd.SwapEffect = SwapEffect.Discard;
scd.Usage = Usage.RenderTargetOutput;

SC = new SwapChain(Parent.DXGIFac, Parent.D3DDev, scd);

var backBuffer = Texture2D.FromSwapChain<Texture2D>(SC, 0);
_rt = new RenderTargetView(Parent.D3DDev, backBuffer);

每次渲染迭代执行的绘图命令很简单:

Parent.D3DDev.ClearRenderTargetView(_rt, new Color4(0, 0, 0, 0));
SC.Present(0, SharpDX.DXGI.PresentFlags.None);

DirectX9 版本非常相似:

设备初始化:

PresentParameters par = new PresentParameters();
par.PresentationInterval = PresentInterval.Immediate;
par.Windowed = true;
par.SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard;
par.PresentationInterval = PresentInterval.Immediate;
par.AutoDepthStencilFormat = SharpDX.Direct3D9.Format.D16;
par.EnableAutoDepthStencil = true;
par.BackBufferFormat = SharpDX.Direct3D9.Format.X8R8G8B8;

// firsthandle is the handle of first rendering window
D3DDev = new SharpDX.Direct3D9.Device(new Direct3D(), 0, DeviceType.Hardware, firsthandle,
    CreateFlags.SoftwareVertexProcessing, par);

渲染窗口初始化:

if (parent.D3DDev.SwapChainCount == 0)
{
    SC = parent.D3DDev.GetSwapChain(0);
}
else
{
    PresentParameters pp = new PresentParameters();
    pp.Windowed = true;
    pp.SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard;
    pp.BackBufferFormat = SharpDX.Direct3D9.Format.X8R8G8B8;
    pp.EnableAutoDepthStencil = true;
    pp.AutoDepthStencilFormat = SharpDX.Direct3D9.Format.D16;
    pp.PresentationInterval = PresentInterval.Immediate;

    SC = new SharpDX.Direct3D9.SwapChain(parent.D3DDev, pp);
}

绘制循环代码:

SharpDX.Direct3D9.Surface bb = SC.GetBackBuffer(0);
Parent.D3DDev.SetRenderTarget(0, bb);

Parent.D3DDev.Clear(ClearFlags.Target, Color.Black, 1f, 0);
SC.Present(Present.None, new SharpDX.Rectangle(), new SharpDX.Rectangle(), HWND);
bb.Dispose();

具有多个交换链和一个设备代码的 C++ DirectX9/Win32 API 测试如下:

[C++] DirectX9 多窗口测试 - Pastebin.com

这是 Kevin Harris 的漂亮示例代码的修改版本。

编辑:

为了清楚起见,我的主要问题不是在进行多窗口渲染时这里的 fps 低,而是对所有操作系统功能(窗口动画、拖放滚动等)造成的一般延迟。

4

5 回答 5

4

这里只说 DirectX,但我记得我们曾经遇到过同样的问题(单台 PC 有 5 个显卡和 9 个屏幕)。

很多时候全屏切换似乎想要在监视器上启用垂直同步,并且由于 Present 无法线程化,具有垂直同步的屏幕越多,每个屏幕的下降幅度就越高(因为您将等待 0 到 16 毫秒)当前通话。

在我们的案例中,我们的解决方案是将窗口创建为最大化并删除边框,这并不理想,但从 10 fps 绘制矩形回到标准速度(60)。

如果您想要代码示例,请告诉我,我会准备一个。

也只是为了测试,我尝试使用 c#/slimdx/dx11 在我的引擎上创建 30 个窗口,渲染一个具有基本着色的球体,仍然远远超过 40 fps。

于 2012-11-03T22:18:31.420 回答
3

We have a similar problem (need to render 3D views on 9+ monitors using 3+ graphics cards). We opted to use raw DirectX11 after finding that 3rd party rendering libraries are all very poor at multiple windows across multiple monitors, let alone with multiple adapters too. (It seems most engines are designed for a fullscreen game, and tend to suck at windowed views). Rather than using a 3rd party layer like SlimDX or SharpDX, we decided in the end to write the core renderer directly in C++ and just expose the simple API that our application needs via C++/CLI - this should maximise performance and minimise maintainability issues (relying on 3rd party vendor for bug fixes etc).

However, just like you, we found in testing that if we rendered 9 views from a single process (each rendered on its own thread), we got terrible performance (very low frame rates). However, if we ran 9 separate processes (one per view/monitor), the performance was as expected (excellent).

So having spent days trawling the net fruitlessly for a better solution, we opted for simply running our renderers in separate processes. Not entirely a bad solution for us as our renderers need to support distribution over multiple PCs anyway, so it just means we'll use this facility permanently instead of only when required.

(I don't know if this is helpful to you as an answer, but we'd also be very keen to know if there are any other solutions out there that work across multiple graphics cards, in case we're missing a better trick)

于 2012-11-03T22:58:49.793 回答
3

从来没有机会运行这种场景,但我唯一确定的是,使用托管包装器绝对没有问题,C++ 代码会遇到完全相同的问题。

此外,在您的描述中,目前还不清楚您的系统上安装了多少显卡。此外,您应该更密切地关注“ DirectX 图形基础结构 (DXGI):最佳实践”,因为它们描述了您可能遇到的许多问题。在全屏中使用不同的显卡运行正确的全屏交换链设置应该没问题(使用“flip”而不是“blit”,请参阅有关此的 msdn 文档),但如果您在最大化窗口中运行您的应用程序,我不认为这种性能会很好,因为 blit 会干扰并产生一些滞后。

您可以使用多个设备完美地拥有一个多线程应用程序,每个线程一个设备,他们应该能够正确安排事情......但同样,由于我在这种情况下没有经验,可能会有某种 GPU 调度在这种特定情况下的问题。

如果即使在仔细遵循 DXGI 设置后问题仍然存在,我建议您使用GPUView调试整个事情,以便更仔细地检查这些问题。它正是针对这种情况而设计的,但是您必须花一些时间来了解如何使用这种工具进行诊断。在上届 GDC 2012 上也有一篇关于 GPUView 的演讲:使用 GPUView 理解你的 DirectX 11 游戏(乔恩故事),这可能值得一读。

于 2012-11-06T12:33:06.323 回答
1

确保您已禁用对本机代码调用的安全检查(通过SuppressUnmanagedCodeSecurityAttribute)。

相关的堆栈遍历是性能杀手。

于 2012-11-03T23:22:22.863 回答
0

使用双缓冲总是一个好主意,因为这可以防止闪烁 - 至少它对 Windows 窗体起作用。

于 2020-10-23T04:17:42.623 回答