Fundamentalmente, um objetivo central na renderização é que cada quadro exibido no monitor apresente uma imagem única e coerente. Existem várias estratégias diferentes que são, ou foram, usadas para conseguir isso.
A seguir, menciono "vsync". Vsync é o momento em que o monitor começa a desenhar uma nova imagem na tela; é o ponto em que o "vblank" começa em uma tela CRT tradicional, onde a linha de varredura momentaneamente para de desenhar e volta para a parte superior do monitor. Este momento é muito importante para muitas das abordagens para enquadrar a coerência.
"Rasgar" é o que chamamos quando uma tela é renderizada a partir de duas imagens diferentes, em um único quadro. Se, por exemplo, desenhei duas imagens de tela que devem ser exibidas uma após a outra, mas o monitor exibiu a metade superior do quadro um e a metade inferior do quadro dois, que está "rasgando". Isso acontece devido à alteração dos dados que o monitor está lendo enquanto o monitor está desenhando, em vez de durante o vblank. (Nos programas modernos, isso normalmente acontece porque o usuário desabilitou a espera do vsync nas configurações de driver)
Zero-Buffer
No hardware mais antigo, geralmente não havia memória suficiente para armazenar uma imagem em tela cheia e, em vez de desenhar uma imagem em tela, era necessário especificar cores para cada linha de digitalização individualmente, enquanto o monitor estava desenhando essa linha. No Atari 2600, por exemplo, você tinha apenas 76 ciclos de instruções da máquina para especificar qual cor entrava em cada pixel da linha de varredura, antes que a televisão começasse a desenhar essa linha de varredura. E então você tinha 76 ciclos de instruções para fornecer o conteúdo para a próxima linha de verificação e assim por diante.
Buffer único
Ao desenhar em um contexto de "buffer único", você está desenhando diretamente na VRAM que está sendo lida pelo monitor. Nesta abordagem, você "corre na linha de verificação". A ideia geral é que, quando a linha de digitalização começar a desenhar o conteúdo do quadro anterior na parte superior da tela, você desenhe o VRAM por trás dele. Portanto, enquanto a linha de digitalização desenha a imagem da tela para o último quadro, você está desenhando o próximo quadro atrás da linha de digitalização.
Em geral, você está tentando terminar de desenhar a imagem do próximo quadro antes da linha de varredura "dobrar" você, contornando os pixels que você está desenhando e também para nunca ficar à frente da linha de varredura, ou então da sua nova o quadro pode ser inserido no que deveria ter sido o quadro anterior.
Por esse motivo, a renderização de buffer único normalmente funcionava desenhando linhas de digitalização, de cima para baixo e da esquerda para a direita. Se você desenhasse em outra ordem, era provável que a linha de varredura aparecesse novamente e localizasse partes da "próxima" imagem que você ainda não desenhou.
Observe que, em sistemas operacionais modernos, você normalmente nunca tem a oportunidade de desenhar um buffer único, embora isso fosse bastante comum trinta anos atrás. (caramba, eu me sinto velho agora - é isso que eu estava fazendo quando comecei no desenvolvimento de jogos)
Double-Buffer
Isso é muito, muito mais simples do que qualquer uma das estratégias que vieram antes.
Em um sistema de buffer duplo, temos memória suficiente para armazenar duas imagens de tela diferentes e, portanto, designamos uma delas como "buffer frontal" e a outra, "buffer de fundo". O "buffer frontal" é o que está sendo exibido no momento, e o "buffer traseiro" é o local onde estamos desenhando.
Depois de terminar de desenhar uma imagem de tela no buffer traseiro, aguardamos até vsync e trocamos os dois buffers. Dessa forma, o buffer traseiro se torna o buffer frontal e vice-versa, e toda a troca ocorreu enquanto o monitor não estava desenhando nada.
Triple-Buffer
Um problema frequentemente levantado com abordagens de buffer duplo é que, depois que terminamos de desenhar o buffer de fundo, precisamos ficar esperando o vsync antes de trocar os buffers e continuar trabalhando; nós poderíamos ter feito cálculos durante esse tempo! Além disso, todo o tempo que esperamos para trocar entre os buffers, a imagem nesse buffer traseiro está ficando cada vez mais antiga, aumentando assim a latência percebida do usuário.
Nos sistemas de buffer triplo, criamos para nós três buffers - um buffer frontal e dois buffers traseiros. A ideia é esta:
O monitor está exibindo o buffer frontal e estamos usando o buffer traseiro nº 1. Se terminarmos de desenhar o buffer traseiro nº 1 antes que o monitor termine de desenhar o buffer frontal, em vez de esperar pelo vsync, começaremos imediatamente a desenhar o próximo quadro no buffer traseiro nº 2. Se terminarmos e o vsync ainda não tiver chegado, começamos a voltar ao buffer número 1 e assim por diante. A idéia é que, quando o vsync finalmente acontecer, um ou outro de nossos buffers traseiros estará completo e esse poderá ser trocado pelo buffer frontal.
O benefício do buffer triplo é que não perdemos o tempo que gastamos esperando o vsync na abordagem de buffer duplo, e a imagem trocada no buffer frontal pode ser "mais atualizada" do que a que estava esperando pelo vsync por 8ms. O lado negativo do buffer triplo é que precisamos de memória extra para armazenar a imagem de tela extra e que o uso de CPU / GPU será maior (novamente, pois não diminuímos o tempo para esperar pelo vsync).
Normalmente, os drivers modernos costumam executar buffer triplo de forma transparente, nos bastidores. Você escreve seu código para fazer buffer duplo, e o driver realmente retorna o controle para você mais cedo e apenas manipula internamente a troca entre o número de buffers que deseja usar, sem que seu código esteja ciente disso.
Atualmente, os fornecedores de GPU recomendam que você não implemente o buffer triplo - o driver fará isso automaticamente.