3.4 Framebuffer
O framebuffer é uma área contígua de memória de vídeo utilizada para armazenar a imagem que será mostrada no dispositivo de exibição. O hardware gráfico lê continuamente o conteúdo do framebuffer e atualiza o dispositivo de exibição, tipicamente a uma taxa entre 60 e 240 Hz nos monitores de LCD.
Nos primeiros PCs e em sistemas gráficos mais antigos, o framebuffer fazia parte da memória do sistema que poderia ser acessada diretamente pela CPU. Nos PCs do início da década de 1990, o framebuffer podia ser acessado com um simples ponteiro para o endereço 0xA000
no chamado “modo 13h” do controlador VGA (um modo gráfico de cores indexadas de 8 bits com resolução de 320x200). Atualmente, o framebuffer é acessado através da GPU e, em GPUs dedicadas, está localizado na memória RAM da placa gráfica.
Em hardware compatível com OpenGL (o que inclui todas as GPUs atuais), o framebuffer pode ser composto por diversos buffers. Pelo menos um deles é um color buffer (buffer de cor) no qual cada pixel contém uma informação de cor, geralmente no formato RGB (24 bits) ou RGBA (32 bits).
Um framebuffer pode ter vários buffers de cor associados. Por exemplo, em implementações que suportam visão estereoscópica, podemos ter um buffer de cor para a tela da visão esquerda e outro para a tela da visão direita. Na técnica de double buffering (descrita no fim da seção), são utilizados dois buffers de cor: o backbuffer, que é um buffer off-screen no qual a imagem é renderizada antes de ser exibida na tela, e o frontbuffer, que recebe o conteúdo do backbuffer ao fim da renderização para exibição na tela. Em renderização estéreo, cada lado esquerdo e direito pode ter o seu backbuffer e frontbuffer (ou outros buffers mais). Cada buffer de cor do framebuffer também pode ser associado a:
- Um depth buffer (buffer de profundidade), no qual cada pixel contém uma informação de profundidade utilizada no teste de profundidade. O teste de profundidade faz parte da implementação da técnica de Z-buffering de determinação de superfícies visíveis. A informação de profundidade pode ser um inteiro ou ponto flutuante de 16, 24 ou 32 bits (geralmente 24 bits).
- Um stencil buffer (buffer de estêncil), utilizado no teste de estêncil para operações de mascaramento e composição de imagens. No buffer estêncil, cada pixel contém um inteiro sem sinal, de 1, 4, 8 ou 16 bits (geralmente 8 bits).
Screen tearing
A taxa de atualização do dispositivo de exibição (chamada de vertical refresh rate) é controlada pelo hardware gráfico. Entretanto, a taxa em que a GPU atualiza o framebuffer pode ser bem maior que a taxa de atualização do dispositivo. Essa taxa é o número de quadros por segundo (FPS) que o processador gráfico é capaz de renderizar. Se o framebuffer for atualizado muito rapidamente, o hardware gráfico pode começar a atualizar o dispositivo de exibição com o conteúdo de um quadro de exibição e terminar com o conteúdo de outro, mais recente. Essa quebra entre os quadros de exibição gera um defeito na imagem conhecido como screen tearing, ou simplesmente tearing (figura 3.39).
Vsync
Para reduzir o problema de tearing, a GPU pode sincronizar o desenho do framebuffer com o tempo de apagamento vertical (VBI), que é o intervalo de tempo definido entre o fim da varredura de um quadro de exibição e o início da varredura do quadro seguinte. Essa sincronização efetivamente limita o número de FPS à frequência do monitor em Hz. Esse processo de sincronização é chamado de sincronização vertical ou Vsync (vertical synchronization). Em monitores mais recentes, compatíveis com as tecnologias G-SYNC da NVIDIA, e FreeSync da AMD, é possível fazer a sincronização na direção contrária: a frequência do monitor é ajustada pela GPU de acordo com a taxa de FPS.
Multiple buffering
O uso de VSync resolve o problema do tearing quando os quadros são renderizados em uma frequência mais alta que a frequência do monitor. Porém, quando a taxa de atualização do framebuffer é menor que a frequência do monitor, o conteúdo parcial do novo quadro pode ser misturado com o conteúdo do quadro anterior, gerando tearing. Uma solução para esse caso é usar a técnica de double buffering.
Double buffering consiste em utilizar dois framebuffers: o backbuffer e o frontbuffer (figura 3.40). A GPU renderiza os gráficos apenas no backbuffer. Enquanto isso, o hardware gráfico atualiza o dispositivo de exibição com o conteúdo do frontbuffer. No próximo VBI, se a renderização no backbuffer ainda não tiver terminado, o mesmo frontbuffer é exibido novamente. Caso contrário, o backbuffer e o frontbuffer trocam de lugar, e agora o frontbuffer torna-se o backbuffer, e vice-versa. Desse modo, o hardware gráfico terá agora no frontbuffer um quadro completo para ser exibido na tela.
Os casos em que a GPU precisa usar o mesmo frontbuffer (porque o backbuffer ainda está sendo desenhado) não são desejáveis pois podem ser percebidos como travamentos da imagem (stuttering), e resultam em uma diminuição da média de quadros por segundo. Isso pode ser melhorado com a adição de mais um backbuffer, no chamado triple buffering. O resultado é uma espécie de fila circular de buffers chamada de swapchain (figura 3.41). No triple buffering, dois quadros consecutivos são desenhados nos backbuffers. No próximo intervalo de apagamento vertical, a fila avança e o backbuffer mais antigo toma o lugar do frontbuffer, que por sua vez torna-se um backbuffer. Esse princípio de uso de múltiplos backbuffers (multiple buffering) pode ser estendido para ainda mais buffers. Entretanto, quanto maior o número de backbuffers, maior o atraso (lag) entre o quadro mais novo renderizado e o quadro que está sendo exibido na tela.