快速多窗口渲染

时间:2012-11-03 21:25:40

标签: c# opengl directx gdi+ directx-9

我已经为C#天搜索和测试不同类型的渲染库了好几周了。到目前为止,我还没有找到一个适用于多窗口渲染设置的单个库。要求是能够在12+监视器设置(财务图表)上运行程序,而无需在快速计算机上进行延迟。每个窗口需要每秒更新多次。虽然执行此CPU需要执行大量密集和时间关键任务,因此必须将一些负担转移到GPU。这就是硬件渲染的步骤,换句话说就是DirectX或OpenGL。

我尝试过使用Windows窗体的GDI +,并认为它对我的需求来说太慢了。我已经通过OpenTK(在Windows窗体控件上)尝试了OpenGL,它看起来相当快(我还有一些测试可以在它上面运行)但是很难正常工作(很难找到/编写好的文本渲染库)。最近我通过SharpDX尝试了DirectX9,DirectX10和Direct2D以及Windows窗体。我为每个窗口尝试了一个单独的设备,并且单个设备/多个交换链接近。所有这些都导致多个窗口的性能非常差。例如,如果我将目标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 Multi-window test - Pastebin.com

这是凯文哈里斯很好的示例代码的修改版本。

编辑:

为了说清楚,我的主要问题是在进行多窗口渲染时不是低fps,这是导致所有操作系统功能(窗口动画,拖动和放弃滚动等)的一般延迟。

5 个答案:

答案 0 :(得分:4)

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

很多时候全屏切换似乎想要在显示器上启用垂直同步,并且由于Present无法进行线程化,因此垂直同步的屏幕越多,您将拥有更高的下降(因为您将在0到16毫秒之间等待)对于每个当前的电话。

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

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

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

答案 1 :(得分:3)

我们遇到了类似的问题(需要使用3个以上的显卡在9个以上的显示器上渲染3D视图)。我们选择使用原始DirectX11后发现第三方渲染库在多个监视器的多个窗口都非常差,更不用说多个适配器了。 (似乎大多数引擎都是为全屏游戏设计的,并且往往会在窗口视图中吮吸)。我们决定直接用C ++编写核心渲染器,而不是使用像SlimDX或SharpDX这样的第三方层,只是通过C ++ / CLI公开我们的应用程序所需的简单API - 这应该最大限度地提高性能并最大限度地减少可维护性问题(依赖在第三方供应商处进行错误修复等)。

但是,就像你一样,我们在测试中发现,如果我们从一个进程渲染了9个视图(每个视图都在自己的线程上渲染),那么我们的性能就会很差(帧速率非常低)。但是,如果我们运行9个独立的进程(每个视图/监视器一个),性能就像预期的那样(优秀)。

因此,为了更好的解决方案花费了数天的时间来搜索网络,我们选择在单独的流程中运行我们的渲染器。对我们来说并不是一个糟糕的解决方案,因为我们的渲染器无论如何都需要支持多台PC上的分发,所以它只是意味着我们将永久使用此工具,而不是仅在需要时使用。

(我不知道这对您是否有帮助,但我们也非常想知道是否有其他解决方案可用于多个显卡,以防我们丢失一个更好的技巧)

答案 2 :(得分:3)

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

另外,在您的描述中,您还不清楚系统上安装了多少个显卡。此外,您应该更密切地关注“DirectX Graphics Infrastructure (DXGI): Best Practices”,因为它们描述了您可能遇到的许多问题。全屏运行不同的显卡,正确的交换链设置为全屏应该是正常的(使用“翻转”而不是“blit”,请参阅msdn doc关于此),但如果你在最大化的窗口中运行你的应用程序,我不认为表现会很好,因为blit会干扰并产生一些滞后。

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

如果在仔细遵循DXGI设置后问题仍然存在,我建议您使用GPUView调试整个问题,以便更仔细地检查这些问题。它适用于这种情况,但您需要花些时间来了解如何使用此类工具进行诊断。在最后GDC 2012: Using GPUView to Understand your DirectX 11 Game (Jon Story)还有一篇关于GPUView的讨论可能值得一读。

答案 3 :(得分:1)

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

相关的堆栈行走是性能杀手。

答案 4 :(得分:0)

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