Aqui você baixa uma pré-encomenda previamente paga. A instalação termina, você inicia o jogo. Tudo está indo bem: o jogo voa com uma taxa de quadros de 60 FPS. Ou pelo menos é o que o contador de quadros em sua sobreposição de GPU informa. Mas algo está errado. Você move o mouse para frente e para trás e percebe que o jogo está ... congelando.
Como isso é possível? Que outro congela a 60 FPS?
Pode parecer ridículo até que você mesmo descubra. Se você conheceu esses frisos, provavelmente já os odiou.
Estes não são atrasos. Não é uma taxa de quadros baixa. Isso é estatístico. Com alto FPS e configuração ultra-rápida ideal.
O que é, de onde veio e há uma maneira de se livrar disso? Vamos descobrir agora.
Desde a introdução das primeiras máquinas de arcade nos anos 70, os videogames rodam a 60 FPS. Normalmente, presume-se que o jogo deve ser executado na mesma velocidade da tela. Foi somente após a popularização dos jogos 3D que tivemos que enfrentar e adotar uma taxa de quadros mais baixa. Na década de 90, quando "placas 3D" (agora chamadas de "GPUs") começaram a substituir a renderização de software, as pessoas estavam jogando a 20 quadros por segundo, e 35 FPS já era considerado a frequência para uma competição de rede séria.
Agora temos carros super-rápidos que podem voar a 60 FPS. No entanto ... parece que há mais desempenho insatisfeito do que nunca. Como isso é possível?
Não é que os jogos não sejam rápidos o suficiente. E o fato de congelarem mesmo com alto desempenho.
Se você navegar nos fóruns de jogos, provavelmente verá algo assim nas manchetes: os
jogadores de PC costumam reclamar que os jogos sofrem de stattering mesmo que não haja problemas de taxa de quadros.
Pode-se presumir que esses são casos isolados, mas essas suposições são dissipadas pelas estatísticas de pesquisa do Google:
Nos últimos 5 anos, a estatização se tornou (relativamente) mais um problema do que o desempenho.
(Observe que esses são valores relativos. Não é que as pessoas procurem informações sobre estatísticas com mais frequência do que sobre taxas de quadros em geral. Embora o número de pesquisas por taxa de quadros permaneça o mesmo, pesquisas por declaração estão se tornando mais comuns, especialmente recentemente. )
Década de busca pelos motivos da declaração
O paciente está definitivamente vivo. Ele apenas se preocupa com frequência.
O autor encontrou esse problema pela primeira vez em 2003, enquanto trabalhava em Serious Sam 2. As pessoas começaram a relatar casos em que os movimentos da tela e do mouse não eram suaves durante o teste em um nível vazio. Isso foi acompanhado por um padrão muito específico no gráfico de taxa de quadros, que a equipe de desenvolvimento chamou de "pulsação".
O primeiro pensamento foi que em algum lugar do código um bug havia se infiltrado, mas ninguém conseguiu encontrá-lo. Parecia que o problema aparecia e desaparecia de forma aleatória - ao reiniciar o jogo, reiniciar o computador ... mas valeu a pena alterar qualquer parâmetro de desempenho e ele desapareceu. Em seguida, você pode alterar o parâmetro de volta e tudo continuou a funcionar perfeitamente. Problema de fantasma.
Obviamente, Sam não era o único problema. Ao lançar outros jogos, apareceu da mesma forma, sugerindo que havia algo de errado com os drivers. Mas o stattering não dependia do fabricante de sua GPU. Ocorreu mesmo com diferentes APIs (OpenGL, DirectX 9, DirectX 11 ...). A única coisa que permaneceu em comum foi que o stattering aparecia aqui e ali em algumas máquinas e cenas de jogo.
Com o lançamento de novos jogos, esse problema continuou a aparecer e desaparecer. Anteriormente, isso afetava apenas alguns usuários, e tudo se limitava a solicitações do suporte técnico para alterar alguns parâmetros de desempenho - o que às vezes ajudava, às vezes não, nunca se sabe.
Então, de repente, em um lindo dia de inverno no início de 2013, os caras da Croteam descobriram outro exemplo desse problema que poderia ser reproduzido de forma relativamente consistente na época - desta vez em um dos níveis em Serious Sam 3. Eles brincaram com aquela cena por um longo tempo, até que de repente aconteceu. Era tão simples - não é de admirar que tenha escapado aos olhos do público por uma década.
Mudando apenas uma opção simples no motor de jogo, eles foram capazes de resolver esse problema. No entanto, imediatamente ficou claro que a solução realmente exigiria muito mais tempo e esforço. E não apenas de uma equipe específica, mas de todo o ecossistema de jogos: desenvolvedores de drivers de GPU, especialistas em manutenção de API, fornecedores de sistemas operacionais - todos.
O que tem acontecido todo esse tempo
É assim que fica quando o jogo fica lento mesmo a 60 FPS. Você poderia ter experimentado algo semelhante jogando qualquer jogo moderno, e provavelmente a primeira coisa que você pensaria é que o jogo não está otimizado. Bem, vamos revisitar essa teoria.
Se o jogo for "muito lento", significa que em alguns pontos ele não será capaz de renderizar um quadro rápido o suficiente e o monitor terá que mostrar o quadro anterior novamente. Portanto, quando gravamos um vídeo a 60 quadros por segundo, ele deve mostrar "quadros perdidos" - quando o próximo quadro não foi exibido a tempo, razão pela qual o mesmo foi mostrado duas vezes.
No entanto, isso só acontece quando você reproduz a animação inteira. Se você examinasse quadro a quadro, não encontraria nenhuma lacuna.
Como isso é possível?
Vamos dar uma olhada nisso mais de perto. Abaixo está uma comparação lado a lado entre o vídeo perfeito e o vídeo encenado:
Seis quadros consecutivos com temporização precisa. Acima - frames corretamente posicionados, abaixo - frames com stattering.
Você pode ver duas coisas aqui: em primeiro lugar, eles realmente funcionam na mesma velocidade: sempre que um novo quadro aparece no topo (correto), então um novo quadro aparece abaixo (stattering). Em segundo lugar, por alguma razão, eles parecem se mover de forma um pouco diferente - há uma "lacuna" perceptível no meio da imagem que oscila entre uma divisão de tempo maior e menor.
Os mais atentos podem notar outro detalhe curioso: a imagem de baixo é supostamente "mais lenta" ... na verdade está "à frente" da correta. Estranho, não é?
Se olharmos para vários quadros consecutivos e seu tempo, podemos ver algo mais interessante: os primeiros dois quadros estão perfeitamente sincronizados, mas no terceiro quadro a árvore no vídeo "mais lento" está significativamente à frente de sua contraparte no "correto" vídeo (circulado em vermelho) ... Você também pode notar que este quadro claramente demorou mais (circulado em amarelo).
Espere, espere ... mas se o vídeo está “mais lento” e o quadro “demorou mais”, como pode prosseguir?
Para entender mais explicações, primeiro você precisa entender como jogos modernos e outros aplicativos 3D geralmente executam animação e renderização.
Uma breve história da sincronização de quadros
Muito tempo atrás, em uma galáxia muito, muito distante ... quando os desenvolvedores criaram os primeiros videogames, eles geralmente o faziam com base na taxa de quadros exata em que a tela era executada. Nas regiões NTSC, onde as TVs operam a 60 Hz, isso significa 60 quadros por segundo, e nas regiões PAL / SECAM, onde as TVs operam a 50 Hz, 50 quadros por segundo.
A maioria dos jogos eram conceitos muito simples executados em hardware fixo - geralmente um console de arcade ou um conhecido "microcomputador doméstico" como o ZX Spectrum, C64, Atari ST, Amstrad CPC 464, Amiga, etc. Assim, criando e testando jogos para uma máquina específica e uma taxa de quadros específica, o desenvolvedor poderia sempre ter 100% de certeza de que a taxa de quadros nunca cairia em lugar nenhum.
As velocidades dos objetos também foram armazenadas em unidades de “pessoal”. Portanto, você não precisava saber quantos pixels por segundo o personagem se moveria, mas quantos pixels no quadro. Por exemplo, em Sonic The Hedgehog para Sega Genesis, essa velocidade é de 16 pixels por quadro. Muitos jogos até tinham versões separadas para regiões PAL e NTSC, onde as animações eram desenhadas à mão especificamente para 50 e 60 FPS, respectivamente. Na verdade, trabalhar em qualquer outra taxa de quadros era simplesmente impossível.
E desde que os jogos começaram a rodar em dispositivos mais variados ao longo do tempo, incluindo PCs com hardware cada vez maior e atualizado, era impossível saber exatamente em que frame rate o jogo rodaria. Este fato foi agravado pelo fato de os próprios jogos terem se tornado mais complexos e imprevisíveis - isso é especialmente perceptível em jogos 3D, onde podem haver grandes diferenças na complexidade da cena, às vezes até determinada pelos próprios jogadores. Por exemplo, todo mundo adora atirar em pilhas de barris de combustível, causando uma série colorida de explosões ... e uma queda inevitável na taxa de quadros. Mas, como é divertido, ninguém se importa.
Portanto, é difícil prever quanto tempo levará para modelar e renderizar um quadro. (Por favor, note que em consoles modernos podemos assumir que o hardware é fixo, mas os jogos em si ainda são bastante imprevisíveis e complexos.)
Se você não tiver certeza de qual taxa de quadros o jogo funcionará, você precisa medir os quadros de frequência atuais e adaptar constantemente a física do jogo e a velocidade da animação para ele. Se um quadro leva 1/60 de segundo (16,67ms) e seu personagem está correndo a 10m / s, então ele se move 1/6 de metro em cada quadro. Mas se o quadro de repente começar a levar 1/30 de segundo (33,33 ms), você terá que mover o personagem já 1/3 metro por quadro (duas vezes "mais rápido") para que ele continue a se mover ao mesmo tempo aparente Rapidez.
Como fazer isso? Como regra, o jogo mede o tempo no início dos quadros adjacentes e calcula a diferença. Este é um método bastante simples, mas funciona muito bem.
Em vez disso, funcionou muito bem antes. Na década de 90, quando 35 FPS era considerado o que velocidade, as pessoas estavam mais do que felizes com isso. Mas, naquela época, as placas de vídeo não eram uma parte tão significativa do PC, e o processador central tinha controle sobre tudo o que acontecia na tela. Se você não tivesse um acelerador 3D, ele até desenhava objetos sozinho. Assim, ele sabia exatamente quando eles atingiriam a tela.
A situação hoje
Com o tempo, as GPUs cada vez mais sofisticadas começaram a aparecer e, inevitavelmente, tornaram-se cada vez mais "assíncronas". Isso significa que, quando a CPU diz à GPU para desenhar algo na tela, a GPU simplesmente armazena esse comando em um buffer para que a CPU possa continuar enquanto a GPU está renderizando. Em última análise, isso leva a uma situação em que a CPU diz à GPU quando o fim do quadro está chegando, e a GPU, ao armazenar isso entre seus dados, não considera isso como algo prioritário - afinal, ainda é processando alguns dos comandos emitidos anteriormente ... Ele mostrará o quadro na tela somente quando tiver feito tudo o que foi carregado antes.
Então, quando o jogo tenta calcular o tempo subtraindo os timestamps no início de dois frames consecutivos, a relevância disso, francamente ... é altamente questionável. Portanto, vamos voltar ao nosso exemplo. Aí tivemos os seguintes frames:
Seis frames consecutivos com temporização precisa. A linha superior está correta, a linha inferior tem um efeito estatístico.
Nos dois primeiros quadros, o tempo de quadro é de 16,67ms (ou 1/60 de segundo) e a câmera se move a mesma quantidade nas maiúsculas e minúsculas, de forma que as árvores estão em sincronia. No terceiro quadro (abaixo, com stattering), o jogo viu que o tempo de quadro é de 24,8 ms (ou seja, mais de 1/60 de um segundo) e, portanto, pensa que a taxa de quadros caiu e corre para alcançar o faltando ... apenas para descobrir que no próximo quadro o tempo é de apenas 10,7 ms, o que faz a câmera diminuir a velocidade, e agora as árvores estão mais ou menos sincronizadas novamente.
O que está acontecendo? Os tempos de quadro medidos por um jogo flutuam devido a vários fatores - especialmente em um sistema multitarefa ocupado como um PC. Portanto, em alguns pontos no tempo, o jogo assume que a frequência caiu de 60 FPS e gera quadros de animação projetados para uma taxa de quadros mais baixa. Mas, devido à natureza assíncrona da GPU, ela sempre retorna aos mesmos 60 quadros por segundo.
Isso é stattering - uma animação gerada para uma taxa de quadros variável (freqüência cardíaca) exibida na taxa de quadros fixa correta real.
Então, em essência, podemos assumir que não há problema - tudo está indo bem, o jogo simplesmente não sabe disso.
Isso nos leva ao que falamos no início do artigo. Quando finalmente descobrimos a causa do problema (embora saibamos que isso é uma ilusão de um problema - realmente não há problema, certo?), Podemos aplicar a seguinte pílula mágica.
O que é essa pílula? No Serious Engine, é denotado como sim_fSyncRate = 60. Em termos simples, isso significa: " ignore completamente todas essas travessuras e finja que sempre medimos 60 frames por segundo estáveis ." E faz com que tudo corra bem - simplesmente porque tudo funcionou bem desde o início! A única razão pela qual a declaração surgiu foi porque o tempo estava incorreto para a animação.
E o quê, isso é tudo?
Então, a solução é simples assim?
Infelizmente não. Foi apenas em testes. Se parássemos de medir a taxa de quadros em condições reais e simplesmente assumíssemos que é sempre igual a 60 FPS, então quando cair abaixo de 60 - e em um PC cairá mais cedo ou mais tarde por qualquer motivo: programas rodando em segundo plano, energia conservação ou proteção contra superaquecimento, quem sabe - então tudo ficará mais lento.
Portanto, se medirmos o tempo de quadro, o stattering ocorre e, se não, em algum ponto tudo pode ficar lento. E depois?
A solução real seria medir não o tempo de início / término de renderização de um quadro, mas o tempo em que a imagem é exibida na tela.
Mas como o jogo pode saber quando um quadro é realmente exibido na tela?
De jeito nenhum: no momento isso é impossível.
Estranho mas verdade. Seria de se esperar que este fosse um recurso central de toda API gráfica. Mas não: eles sofreram mudanças em todos os outros aspectos além deste. Não há como saber com certeza quando o quadro realmente aparecerá na tela. Você pode descobrir quando a renderização terminou. Mas isso não é o mesmo que tempo de exibição.
O que agora?
Bem, não é tão ruim. Muitas pessoas estão trabalhando ativamente na implementação de suporte para a sincronização correta de quadros para diferentes APIs. A API Vulkan já tem uma extensão chamada VK_GOOGLE_display_timing que tem um histórico comprovado de implementação desse conceito, mas está disponível apenas para um número limitado de dispositivos.
O trabalho está em andamento para fornecer soluções semelhantes e melhores, eu gostaria de acreditar que já em todas as principais APIs gráficas. Quando? É difícil dizer, porque o problema afeta profundamente vários subsistemas do sistema operacional.
No entanto, esperamos que em breve esteja disponível para o público em geral.
Várias ressalvas e outros detalhes
Assumiremos que este é o final do texto principal. As seções abaixo são "recursos bônus" amplamente independentes uns dos outros e dos itens acima.
"Compositor"
Isso é um efeito de vidro fosco? Sim, é por isso que temos que ter um compositor. Muito importante, não é?
Um conceito chamado Gerenciador de janelas de composição , também conhecido como compositor, está envolvido nos bastidores . Este é um sistema que agora está presente em todos os sistemas operacionais e permite que as janelas sejam transparentes, tenham um fundo desfocado, sombras, etc. Os compositores podem ir mais longe e mostrar as janelas do seu programa em 3D. Para fazer isso, o compositor assume o controle da última parte do quadro e decide o que fazer com ele, pouco antes de atingir o monitor.
Em alguns sistemas operacionais, o compositor pode ser desativado no modo de tela inteira. Mas nem sempre isso é possível, e mesmo nesses casos - não podemos rodar o jogo no modo de janela?
Complexidade de renderização de gerenciamento de energia e térmico VS
Devemos também levar em consideração o fato de que CPUs e GPUs modernos não operam em uma frequência fixa, mas ambos possuem sistemas que ajustam sua velocidade para cima e para baixo dependendo da carga e da temperatura atual. Assim, o jogo não pode simplesmente assumir que eles terão a mesma velocidade quadro a quadro. Por outro lado, o sistema operacional e os drivers não podem esperar que o jogo faça a mesma quantidade de trabalho a cada quadro. Sistemas complexos de comunicação entre duas partes devem ser projetados de forma que tudo isso seja levado em consideração.
Não poderíamos apenas ...
Não poderia. :) Normalmente, a medição de tempo da GPU é oferecida como uma alternativa para exibir o tempo. Mas isso não leva em consideração a presença de um compositor e o fato de que nenhum dos timers de renderização da GPU realmente sincroniza diretamente com a atualização do display. Para uma animação perfeita, definitivamente precisamos saber exatamente quando a imagem foi exibida, não quando terminou de renderizar.