Os dados da GUI acelerada por hardware são mantidos na GPU

8

Estou fazendo uma pesquisa sobre como a maioria das bibliotecas GUI aceleradas por hardware funcionam. Na verdade, só me preocupo com os recursos de renderização deles aqui. Estou tentando descobrir qual seria a melhor maneira de tentar escrever o meu próprio como uma espécie de projeto paralelo. Estou tentando obter o melhor desempenho aqui, em vez de recursos excessivamente sofisticados. Quero desenhar primitivos, texto e animação.

Algumas boas bibliotecas que eu conheço são Qt, Skia e Cairo (embora não tenha certeza de qual é o estado da HWA). Também observei o NanoVG, que é uma pequena biblioteca que parece ter um número razoável de seguidores. Não consegui alcançar um desempenho decente com o NanoVG ...

A única coisa que me impressionou foi que todas essas bibliotecas parecem fazer uso do conceito de "pintura", onde parece que cada forma primitiva é desenhada do zero várias vezes. O que quero dizer com isso é que, a partir das APIs, não parece que as formas sejam criadas como "objetos" na GPU ou seja qual for a terminologia e depois deixadas para serem renderizadas "automaticamente". Em outras palavras, eles não são deixados na memória da GPU por serem redesenhados em algum loop grande. Para elaborar, parece que, para cada retângulo que precisa ser desenhado, um estado OpenGL inteiro é configurado apenas para renderizar esse retângulo e depois é destruído novamente. Parece que essas formas renderizadas são renderizadas pelo menos em seus destinos finais, permitindo que a GPU componha a cena inteira.

A maneira como eu esperava que essas bibliotecas funcionassem é armazenando a cena inteira na GPU (desculpe a horrível terminologia). Por exemplo, as primitivas seriam trianguladas e deixadas na memória onde, após algum processo complexo, seria usado para ter um loop de renderização principal para a cena. Além disso, haveria mecanismos para atualizar atributos ou excluir ou adicionar primitivas. Esta é uma descrição bastante vaga, mas acho que você entendeu.

O que eu gostaria de perguntar agora é se existe algum benefício no desempenho da abordagem "pintura" em comparação à abordagem "salva" (novamente, não faço ideia se existem nomes adequados para essas coisas ...). Alguns mecanismos de cache intrincados, talvez? Ou isso é muito mais simples de se trabalhar?

Sei que a abordagem "salva" pode usar mais memória na GPU, mas todas as chamadas do OpenGL necessárias para a abordagem "pintura" não são muito caras? Eu acho que alguém pode compensar isso armazenando em cache as formas renderizadas, mas a GPU realmente oferece um benefício tão grande ao fazer uma rasterização única (ou não muito regular) em comparação com a CPU, especialmente devido à comunicação a sobrecarga? Além disso, essa sobrecarga de comunicação não apresenta sérios problemas para animações quando o desenho precisa ser feito para cada quadro?

Estou certo de que o NanoVG não possui um mecanismo interno de armazenamento em cache e eu assumiria que isso poderia ser responsável por seu desempenho bastante sem brilho. O Qt, por outro lado, parece ter um excelente desempenho, portanto deve estar fazendo algo certo. O Google também parece ser capaz de fazer bom uso do Skia.

PS. Não sou profissional de nenhum tipo e só recentemente comecei a aprender o OpenGL.

Edição: Outra possibilidade que eu pensei é que talvez a abordagem "pintura" foi considerada necessária apenas por causa dos benefícios de memória? A razão pela qual eu acho que é porque todas essas bibliotecas foram iniciadas em uma era diferente e também têm como alvo plataformas incorporadas, o que significa que a memória da GPU pode ser tão escassa nas plataformas de destino e que usar o mínimo possível delas mais importante que o desempenho. Mais uma vez, porém, em uma situação como essa, não estou convencido de que a rasterização de GPU quadro a quadro, devido à sobrecarga de comunicação, supere a CPU, especialmente considerando a provavelmente baixa contagem de pixels em plataformas com pouca memória.

Além disso, li em http://blog.qt.io/blog/2010/01/06/qt-graphics-and-performance-opengl/ que o Qt aparentemente combina código shader de segmentos pré-codificados em tempo de execução antes de "pintar" e então espera que o compilador OGL incline o código corretamente no tempo de execução. Isso soa como ainda mais sobrecarga de inicialização do OGL para mim ...

Gerharddc
fonte
2
TL; DR; não faria mal aqui
Kromster 10/09/2015

Respostas:

6

Salvar a janela inteira como um único objeto na GPU (seria um monte de retângulos salvos como VBO) e renderizá-la em uma única chamada de OpenGL seria rápido, mas tem várias desvantagens:

  • Toda a geometria teria que ser renderizada usando um sombreador único. Ter shaders separados (para cópia opaca, cópia transparente, gradiente, ...) é mais útil.
  • Toda a geometria pode ser usada apenas a partir de uma quantidade limitada de texturas. Mesmo se você usar atlas, precisará de muitas texturas para a GUI. (Partes do tema da GUI, ícones, fontes, ...)
  • Você precisa reconstruir e recarregar todo o objeto na GPU após cada alteração.
  • Todo widget deve ser capaz de produzir seu pedaço de geometria que é mais difícil de abstrair do que a pintura 2D.
  • Em algumas GPUs, você pode renderizar objetos 2D (preenchendo a área com cores, copiando de imagem para imagem, ...) com comandos 2D, mais rápidos do que usar o pipeline 3D.

Se você o dividir em vários objetos, acabará com um ou poucos retângulos por objeto. É mais fácil e rápido renderizá-los sem nenhum objeto armazenado.

O que as estruturas da GUI fazem é rastrear quais partes exatas da janela foram alteradas e repintar apenas elas. A imagem antiga é armazenada em cache na GPU. Essa abordagem pode ser usada com vários recursos de desenho, não apenas com a renderização acelerada do OpenGL / DirectX.

Se você quiser verificar o exemplo de uma biblioteca de GUI que gera geometrias que podem ser alimentadas no OpengGL (ou em diferentes APIs 3D), consulte o librocket . Na verdade, ele pode agrupar geometrias estáticas e renderizá-las em uma chamada de desenho único, mas qualquer elemento que mude com frequência ou precise renderizar com seu próprio shader precisa permanecer separado.

michalsrb
fonte
ok obrigado. Todo o quadro anterior é armazenado em cache como imagem ou todos os elementos são armazenados em cache em um atlas? A razão pela qual estou perguntando é porque não vejo como o cache do quadro suportaria, por exemplo, a tradução de um único elemento. Manter um atlas, é claro, seria muito esforço.
Gerharddc 10/09/2015
Todo o conteúdo da janela é normalmente armazenado em cache. Aposto que algumas estruturas de GUI também armazenam alguns estágios intermediários, o conteúdo de qualquer coisa rolável seria um bom candidato. Não acho que a tradução seja uma operação comum na GUI, além das áreas de rolagem. A maioria das repaints é pequena e no lugar causada pela mudança de widgets no hower ou clique, alteração de texto, ... Coisas como diálogos e menus são janelas tecnicamente separadas, compositores de janelas modernos cuidam de movê-los sem repainas.
michalsrb
Eu acho que isso faz sentido. Isso significaria que toda a janela teria que ser redesenhada se eu movesse algo inesperadamente como um botão?
Gerharddc 10/09
Isso realmente depende de como a estrutura específica é implementada. Mas é provável que mover um botão acione algum tipo de atualização de layout e grande parte da janela, se não toda, terá que ser repintada.
michalsrb