Eu acho que você está confundindo 'framebuffer' e 'um grande monitor'. Você também está assumindo que o espaço do usuário pode acessar diretamente o framebuffer na placa gráfica.
Assuma o mais básico das janelas: uma imagem simples. Para o sistema operacional, isso é simplesmente um bloco de memória principal em que o espaço do usuário pode desenhar pixels. Quando sinalizado pelo sistema de janelas / sistema operacional, o driver gráfico copia esses pixels para o buffer de quadros da placa gráfica, no espaço / ordem correta.
No pseudo-código:
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
...
}
}
Um driver da placa gráfica pode permitir que você faça essa operação memcpy
com muito mais eficiência, por exemplo, simplesmente copiando a estrutura WINDOW para a memória da placa gráfica e fazendo a complicada cópia linha a linha no hardware.
Em uma configuração de vários monitores, basta repetir o processo para cada placa gráfica, copiando apenas a parte da janela que é exibida nesse monitor específico.
Mais uma vez, no pseudo-código:
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
...
}
}
Você poderia dizer que esse sistema é complicado. E, de fato, foi simplificado usando DirectDraw
, que permite alocar imediatamente um bloco de memória no buffer de quadros da placa gráfica. No entanto, esse bloco está ligado a uma placa gráfica, e é por isso que você vê uma tela verde ao arrastar um reprodutor de mídia usando o DirectDraw para o segundo monitor.
Nota: Não tenho ideia de como isso funciona no Windows 7 ou em outros ambientes de janela 3D. Eu sei que eles escrevem o framebuffer da janela em uma textura e o renderizam em 3D. Talvez a textura seja copiada para a outra placa gráfica quando você move a janela?
Por fim, você se refere ao SLI ou a outros sistemas. Isso não tem nada a ver com renderização 2D. O que acontece é que você agrupa a memória de ambos os cartões. Em seguida, você instrui cada GPU a renderizar apenas uma parte da tela. A segunda placa gráfica (na qual nenhum monitor está conectado) grava o resultado de seus cálculos (pixels!) No buffer de quadros da placa gráfica principal, após o qual o chip VGA ou DVI envia isso para o monitor. A razão pela qual uma ponte é necessária é 1) copiar somente os dados de textura e modelo uma vez, mesmo que seja usada pelas GPUs e 2) para permitir que a segunda GPU grave os pixels no framebuffer da primeira.
Você também se refere ao Nvidia Optimus ea. Esse sistema é realmente muito semelhante ao SLI. No entanto, a ponte só é necessária para permitir que a segunda GPU grave pixels no framebuffer. Os chips não compartilham qualquer textura ou dados de vértice e não podem renderizar uma cena 3D em colaboração.