Por que ainda existem artefatos visuais quando os emuladores de terminal desenham aplicativos baseados em texto? Isso em computadores recentes que renderizam jogos 3D e janelas GUI, incluindo fontes vetoriais com suavização de serrilhado sem artefatos.
Vejo regularmente os seguintes artefatos que revelam etapas intermediárias do processo de atualização de tela:
- Movimento do cursor do terminal (o cursor pisca ou salta pela tela durante a atualização)
- Rasgando (parte da tela mostra coisas antigas enquanto outra parte mostra coisas novas)
- Rolagem (a rolagem é perceptível, em vez da nova posição de rolagem ser mostrada imediatamente)
Os artefatos são vistos apenas por intervalos de menos de um segundo, e não durante a maioria das atualizações de tela, mas tendo crescido em GUIs sem cintilação, eu ainda gostaria de saber como evitá-los. Todos os artefatos acima (exceto rolagem) podem ser vistos, por exemplo, no vídeo ASCIInema a seguir, uma vez que ele começa a desenhar as telas mais complexas: MapSCII - o mundo inteiro em seu console!
Também não estou falando especificamente sobre atualizações lentas. Seria bom se as atualizações fossem sempre instantâneas, mas isso nem sempre é possível devido a atrasos na rede e no processamento. O que quero dizer aqui é que telas parcialmente desenhadas geralmente são visíveis por um breve momento. Na maioria das GUIs modernas, apenas telas totalmente finalizadas são mostradas ao usuário, e artefatos de desenho parcial são muito raros.
É minha impressão que o pipeline de emulação de terminal é algo assim:
- O usuário pressiona uma tecla no teclado
- Kernel passa o pressionamento de tecla do driver do teclado para o sistema de janelas
- O sistema de janelas passa o pressionamento de tecla para o emulador de terminal
- O emulador de terminal passa o pressionamento de tecla para o dispositivo de kernel pseudoterminal (pty)
- Pty interpreta o pressionamento de tecla e passa o resultado para o aplicativo baseado em texto
- O aplicativo executa o comando em resposta ao pressionamento de tecla
- O aplicativo renderiza uma nova tela (grade de células de caracteres) para o buffer interno
- Chamadas de aplicativos
curses
ou outra biblioteca para converter a grade de células de caracteres em códigos de escape ANSI que renderizarão uma tela equivalente no terminal - A biblioteca grava esses códigos de escape ANSI no dispositivo pty
- Pty processa os dados escritos de alguma forma
- O emulador de terminal lê dados processados de pty em alguns pedaços
- O emulador de terminal chama o sistema de janelas para renderizar o resultado dos códigos de escape ANSI na janela do terminal
Qual das etapas acima pode retardar o processo o suficiente para que o emulador de terminal nos mostre etapas intermediárias de renderização em vez de mostrar apenas o resultado final?
Parece que a velocidade dos terminais de hardware (conexões de porta serial) é ditada por sua taxa de transmissão, que pode ser alterada
tcsetattr()
, mas li em várias fontes que a configuração da taxa de transmissão não tem efeito nos dispositivos pseudoterminais (pty) usados pelo terminal emuladores. Isso significa que os kernels Unix não limitam deliberadamente as comunicações pty?Os aplicativos ou bibliotecas de renderização (curses, etc.) enviam texto e códigos ANSI em várias gravações em vez de tentar se contentar com apenas uma
write()
?Os kernels Unix têm limites de tamanho em seus buffers de E/S internos, o que afeta coisas como a quantidade máxima de dados que podem ser enviados por um canal sem bloqueio. Isso afeta a renderização de telas de terminal com muitos detalhes (uma tela cheia de texto, muitas cores etc.)? Imagino que o texto combinado e os códigos de escape ANSI poderiam somar tantos dados que não cabem no buffer do driver pty, o que dividiria uma atualização de tela em várias operações de gravação pelo aplicativo e várias leituras pelo emulador de terminal. Se o emulador de terminal estivesse ansioso para exibir os resultados de cada leitura antes de processar a próxima, isso faria com que a exibição piscasse até que a leitura final em um lote fosse processada.
Os emuladores de terminal ou drivers pty têm tempos limite deliberados para processamento em lote, de modo que seu comportamento imite mais de perto os terminais de hardware, pareça mais natural ou aborde alguma outra preocupação que foi considerada mais importante do que a velocidade de exibição?
Recentemente tem havido algum esforço para fazer novos emuladores de terminal que renderizem mais rápido (por exemplo, pré-renderizando fontes em texturas OpenGL na memória de vídeo). Mas esses esforços só parecem acelerar a renderização de uma grade de células de caracteres em um bitmap de tela uma vez que a grade foi calculada.
Parece haver algo mais acontecendo que torna essas coisas fundamentalmente lentas, mesmo em um computador muito rápido. Pense nisso: se o emulador de terminal processa todos os códigos ANSI para obter uma grade de células de caracteres antes de renderizar qualquer coisa em um bitmap de tela, então não importa quão lentas sejam as rotinas de renderização de grade de caracteres para bitmap - deve haver sem cintilação (pelo menos não o tipo de cintilação que claramente parece corresponder ao movimento do cursor em um terminal de hardware, que é o que vemos com frequência). Mesmo que o emulador de terminal levasse um segundo inteiro para desenhar qualquer grade de células de caracteres na tela, simplesmente teríamos um segundo de inatividade, não um segundo de cintilação.
Um problema semelhante é que o Unix clear
e os reset
comandos são incrivelmente lentos para o que fazem (da perspectiva de um usuário de GUI, eles não fazem nada mais complexo do que redesenhar um bitmap). Talvez por motivos relacionados.
Eu adoraria ouvir mais detalhes sobre como exatamente acionar essas cintilações proeminentes, pois não noto nenhum ao usar meu sistema.
No meu sistema, o VTE (o mecanismo por trás do GNOME Terminal) pode processar cerca de 10 MB/s de dados recebidos. O desempenho de outros emuladores também não está muito longe disso, talvez dentro de um fator de 3 ou 5 em ambas as direções. Isso muito, deve ser mais do que suficiente para atualizações sem movimentos.
Tenha em mente que um terminal de tela cheia pode conter algumas dezenas de milhares de células de caracteres. Os caracteres UTF-8 consistem em vários bytes. Alternar para diferentes atributos (cores, negrito etc.) requer sequências de escape que podem ir de 3 a 4 a facilmente 10 a 20 bytes (especialmente com extensões de 256 cores e truecolor). Layouts verdadeiramente complexos podem, portanto, exigir 100 kB ou uma quantidade ainda maior de tráfego. Isso com certeza não pode ser passado pela linha tty em uma única etapa. Estou até incerto se certos aplicativos (ou bibliotecas de desenho de tela) cuidam de armazenar em buffer toda a saída em uma única etapa. Talvez eles apenas usem printf() e deixem o stdio liberá-los a cada 8 kB ou mais. Esta poderia ser outra razão para eles serem um pouco lentos.
Eu não estou realmente familiarizado com o comportamento de agendamento do kernel, por exemplo, se ele precisa alternar entre os dois processos, bem como os modos de usuário/kernel, ou se eles podem ser executados simultaneamente em uma CPU multi-thread. Eu realmente espero que eles possam rodar simultaneamente em uma CPU multi-core, que a maioria das CPUs são hoje em dia.
Não há estrangulamento intencional na história. No entanto, pode haver suposições quando os emuladores decidem continuar lendo os dados ou atualizar sua tela. Por exemplo, se o emulador de terminal processar a entrada mais rapidamente do que o aplicativo emite, ele a verá travando após o processamento do primeiro bloco e, portanto, poderá decidir razoavelmente atualizar sua interface do usuário.
O cursor é provavelmente o mais proeminente a piscar, uma vez que o cursor se move ao longo da tela à medida que o conteúdo está sendo atualizado. Não pode ficar no mesmo lugar. Se o emulador atualizar sua tela apenas uma vez ao receber os dados de entrada e o cursor eventualmente permanecer no mesmo local, essa cintilação provavelmente se tornará visível.
Você pode estar interessado nesta proposta de atualizações atômicas ( discussões aqui ) que resolveriam principalmente esse problema, se suportadas tanto pelo emulador de terminal quanto pelo aplicativo em execução.
Você também pode estar interessado em saber por que a experiência de rolagem com o teclado é necessariamente irregular devido a uma interferência entre a taxa de repetição do teclado e a taxa de atualização do monitor, algo que não está piscando em si, mas causa uma experiência desagradável.