无需 SLI 即可实现双 Nvidia 卡 Framebuffer 组合的机制是什么?

all*_*tic 5 windows multiple-monitors gpu

首先,让我们确保我们都在同一页面上:

作为一些背景信息,请注意,当您启动操作系统(任何操作系统)时,BIOS(或 UEFI GOP)会告诉操作系统哪个图形适配器应用作主帧缓冲区。主帧缓冲区基本上是虚拟内存的一个区域,写入时会导致图形驱动程序启动 DMA 传输以将帧数据发送到图形输出设备。显然,在多 GPU 配置中,事情并不是这么简单,但总体思路是,基本级别的操作系统只知道一个帧缓冲区。为了确定帧缓冲区的构成,插入同一显卡的显示器被视为由同一帧缓冲区驱动。默认情况下,插入不同卡的监视器由不同的帧缓冲区驱动。目前有一些技术技巧可以帮助弥合不同帧缓冲区之间的硬件差距;因此我的问题...

假设您购买的显示器数量多于任何一张显卡上的端口数量。例如,如果您的显卡有一个端口,那么您就有两台显示器。如果您的显卡有两个端口,则您将拥有三个显示器。等等。

还假设您想要 Eyefinity 或类似的设置,其中操作系统将所有显示器视为一台“大显示器”。

确实希望能够在不同的显示器之间无缝地拖动鼠标和窗口。

执行此操作的方法:

  1. 物理显卡桥接:Nvidia SLI 或 AMD CrossFire。这些解决方案将允许您将“额外的显示器”插入第二个独立显卡。两个显卡使用专用桥硬件(或者在最新一代 AMD Radeons 的情况下,使用 PCIe 总线)相互通信。

  2. 平台硬件辅助帧缓冲区共享:Nvidia Optimus、AMD Enduro、LucidLogix Virtu MVP...概念是相同的。您将显示器插入一张卡(通常是主板,用于使用处理器的 iGPU),并将显示器插入独立显卡。主板上的某些芯片有助于协调和同步这两个独立的显卡,使操作系统产生只有一个帧缓冲区的错觉,从而您可以进行多显示器设置。请注意,其中一些解决方案还可以控制从哪个 GPU 渲染帧,而不仅仅是控制输出帧光栅化到的位置。

  3. 软件?:如果前两种硬件方案都不可用,显然还有办法。例如,如果您的主板没有Nvidia Optimus 或 LucidLogix Virtu MVP;并且您的卡属于 SLI;例如,您仍然可以将 Nvidia GTX 280 和 Nvidia GT 210 插入同一台机器,在多显示器用户体验方面您将获得相同的结果。您可以在显示器之间无缝移动鼠标和窗口。

我的问题是,在上面的第三个选项“软件?”中,它在 Windows 上到底如何工作?另外,那个特定的机制/功能叫什么?

  • 它是特定于供应商的图形驱动程序的功能吗?
  • 它是内置于 Windows 本身中的吗?
  • 这该死的东西叫什么?

小智 2

我认为您混淆了“帧缓冲区”和“一台大显示器”。您还假设用户空间可以直接访问显卡上的帧缓冲区。

假设最基本的窗口:一个简单的图像。对于操作系统来说,这只是用户空间可以在其中绘制像素的主内存块。当操作系统/窗口系统发出信号时,图形驱动程序以正确的空间/顺序将这些像素复制到图形卡的帧缓冲区中。

在伪代码中:

int* MAIN_FRAMEBUFFER;
int RESOLUTION_X, RESOLUTION_Y;
struct WINDOW = {
  int x, y;
  int width, height;
  int* pixels; // virtual framebuffer
}
WINDOW[] all_windows;
int nr_of_windows
void draw_all_windows() {
    for(int i=0; i<nr_of_windows; i++) {
       // transfer the virtual framebuffer of every window to the graphics card
       WINDOW w = all_windows[i];
       for(int y=w.y; y<w.y+w.height; y++) {
           memcpy(&w.pixels, &MAIN_FRAMEBUFFER + w.x + y*RESOLUTION_X, w.width);
       }
       // Draw window manager decoration
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

显卡驱动程序可以让您memcpy更有效地执行此操作,例如,只需将 WINDOW 结构复制到显卡内存中,并在硬件中进行复杂的逐行复制。

在多显示器设置中,您只需对每个显卡重复该过程,仅复制该特定显示器上显示的窗口部分。

再次用伪代码表示:

int* MAIN_FRAMEBUFFER1;
int RESOLUTION_X, RESOLUTION_Y;
// assume FRAMEBUFFER2 is to the right of FRAMEBUFFER1, and they have the same resolution
// the pseudo-code can be extended to allow a different resolution and orientation
int* MAIN_FRAMEBUFFER2;
struct WINDOW = {
  int x, y;
  int width, height;
  int* pixels; // virtual framebuffer
}
WINDOW[] all_windows;
int nr_of_windows
void draw_all_windows() {
    for(int i=0; i<nr_of_windows; i++) {
       // transfer the virtual framebuffer of every window to the graphics card
       WINDOW w = all_windows[i];
       for(int y=w.y; y<w.y+w.height; y++) {
           if(w.x + w.width < RESOLUTION_X) {
               // fully on monitor 1
               memcpy(&w.pixels, &MAIN_FRAMEBUFFER1 + w.x + y*RESOLUTION_X, w.width);
           } else if (w.x > RESOLUTION_X) {
               // fully on monitor 2
               memcpy(&w.pixels, &MAIN_FRAMEBUFFER2 + (w.x - RESOLUTION_X) + y*RESOLUTION_X, w.width);
           } else {
               // split between monitor1 and monitor2
               int monitor1_w_width = RESOLUTION_X - w.x;
               memcpy(&w.pixels, &MAIN_FRAMEBUFFER1 + w.x + y*RESOLUTION_X, monitor1_w_width);
               memcpy(&w.pixels + monitor1_w_width, &MAIN_FRAMEBUFFER2 + monitor1_w_width + (w.x - RESOLUTION_X) + y*RESOLUTION_X, w.width - monitor1_w_width);
           }
       }
       // Draw window manager decoration
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

可以说这个系统很复杂。事实上,它已经通过使用进行了简化DirectDraw,它允许您立即在显卡的帧缓冲区中分配一块内存。但是,此块与一个显卡相关联,这就是当您使用 DirectDraw 将媒体播放器拖动到第二个显示器时会看到绿屏的原因。

注意:我不知道这在 Windows 7 或其他 3D 窗口环境中如何工作。我知道他们在纹理中写入窗口帧缓冲区并以 3D 形式渲染它。当您移动窗口时,纹理可能会复制到其他显卡吗?

最后,你指的是SLI或者其他系统。这与 2D 渲染无关。发生的情况是将两张卡的内存集中在一起。然后,您指示每个 GPU 仅渲染屏幕的一部分。第二个显卡(未连接显示器)将其计算结果(像素!)写入主显卡的帧缓冲区,然后 VGA 或 DVI 芯片将其推送到显示器。需要桥的原因是 1) 仅复制纹理和模型数据一次,即使两个 GPU 都使用它;2) 允许第二个 GPU 将像素写入第一个 GPU 的帧缓冲区。

您还可以参考 Nvidia Optimus ea。该系统实际上与 SLI 非常相似。然而,桥接器仅需要允许第二个 GPU 将像素写入帧缓冲区。这些芯片不共享任何纹理或顶点数据,并且无法协作渲染 3D 场景。