Pixi.js: Proposta: Melhorar a renderização do texto

Criado em 5 abr. 2020  ·  27Comentários  ·  Fonte: pixijs/pixi.js

A comunidade já disse isso - o desempenho de renderização de texto do Pixi precisa melhorar. Esta edição é dedicada a como podemos melhorar e o que eu acho que será a melhor maneira de entregar:

  • Campos de distância sinalizada: esta técnica renderiza o texto em uma textura (usando a API 2D do Canvas) e aplica uma transformação de distância. Em termos simples, a transformação de distância definirá o valor de cada pixel na textura de saída para a distância desse pixel até o contorno mais próximo do texto na textura de entrada. pixi-sdf-text é um exemplo: https://github.com/PixelsCommander/pixi-sdf-text

  • VTMs: mapas de textura vetorial codificam descontinuidades de curva em nível de pixel em texturas.

  • Renderização exata da curva de Bezier: renderiza a fonte "como está", tesselando tudo, exceto as curvas. As curvas são renderizadas usando um sombreador de fragmento especial (nenhuma amostra da curva é feita, ela é renderizada exatamente na GPU com antialiasing): https://www.microsoft.com/en-us/research/wp-content/uploads /2005/01/p1000-loop.pdf. Criei uma demonstração para a curva de Bezier quadrática: https://codepen.io/sukantpal/pen/GRJawBg?editors=0010

@bigtimebuddy e eu discutimos esses métodos e achamos que a terceira abordagem é a melhor porque:

  • SDFs requerem atlas pré-gerados. Gerar SDFs multicanais é muito complicado (se quisermos fazê-lo em tempo de execução).

  • VTMs são muito complicados para apenas fontes.

  • As curvas bezier exatas requerem apenas que você renderize a fonte como um caminho como tudo o mais em Graphics.

Comentários muito úteis

@eXponenta, por favor, pare de ser tão hostil. Estamos explorando abordagens, mas nada é sólido ainda. Por favor, seja construtivo e não use insultos.

Todos 27 comentários

Não deve estar no núcleo.
É um pacote pesado e DEVE SER implementado fora do pacote principal, a equipe principal não deve se preocupar com sua implementação.

Pixi SDF é o exemplo certo.

Por favor, não tente fazer Phaser de PIXI

Eu gostaria de dar um pouco mais de contexto. Pedi ao @SukantPal para pesquisar abordagens alternativas de Texto / BitmapText que têm diferentes compensações. Particularmente, se pudermos encontrar algo que funcione melhor do que Texto e tenha uma ótima aparência.

Acho que existem várias abordagens aqui. Alguns podem ser apropriados como plug-ins de terceira parte, alguns podem estar no repo, mas não agrupados, e alguns podem substituir / aumentar a API atual. Vamos descobrir qual é a abordagem de renderização de texto mais frutífera que otimiza o desempenho, mas com menos compensação para BitmapText.

Existem alguns critérios que eu usaria para julgar um bom objeto de exibição de texto:

  • Bom desempenho de tempo de execução - Renderizar e alterar texto é de baixo custo
  • Pouca memória - o uso de RAM é baixo
  • Dependências pequenas - o tamanho do arquivo da pegada de dependência é pequeno
  • Grande fidelidade - funciona bem com diferentes resoluções
  • Estilo flexível - suporte para traços, sombras, gradientes
  • Compatível com idiomas de glifos grandes - por exemplo, chinês, japonês, coreano
  • Compatível com versões anteriores - Funciona com Context2D

| | Perf | Memória | Dependências | Fidelidade | Styling | CJK | Context2d |
| - | - | - | - | - | - | - | - |
| Texto | 👎 | 👎 | 👍 | 👍 | 👍 | 👍 | 👍 | 👍 |
| BitmapText | 👍 | 👎 | 👍 | 👎 | 👎 | 👎 | 👍 |

OK.
Descreve fragment interpolation e todas as implementações não nativas:

  1. Precisa de shader extra e dados extras - isso evitará o envio em lote.
  2. Necessita reconstruir a geometria quando muda (p.3).
  3. Exigir uma tabela de glifos para todos os glifos existentes na linguagem, que exigem o carregamento de TTF específico para a memória totalmente, porque não podem suportar streaming e, em seguida, analisá-lo. Parece um SWF.
    Porque não podemos carregar a fonte do sistema como forma - não podemos usá-la.
  4. As regras específicas do idioma estão mudando para a equipe lib da equipe do navegador, que implementa regras de renderização: ltr / rtl, regras de concatenação de glifos hindi / árabe, etc ... o mesmo que mover 10% do navegador para lib. O Unity ainda não foi implementado da maneira certa.
  5. Shader pesado com argumentos extras para apoiar estilos. Isso aumenta o custo de renderização, porque devemos renderizá-lo em tempo real.
    E, claro, devemos armazená-lo em cache como textura.

@eXponenta

  • O texto pode ser agrupado - embora não com outras coisas como gráficos.

  • A implementação do Texto atual armazena em cache cada glifo várias vezes (se uma letra for usada várias vezes em vários PIXI.Texts). Ter uma geometria por glifo é melhor. Além disso, você não precisa armazenar em cache em tamanhos de fonte diferentes.

  • A geometria não precisa ser reconstruída, digamos - se as letras no texto mudarem, você precisará buscar novamente na tabela de geometria de glifo. Se a transformação mudar, você só precisa mudar um uniforme.

  • Podemos usar uma biblioteca para análise de fontes. A conversão para geometria é trivial.

  • Não sei o que são as regras de concatenação de glifos. Você poderia explicar?

  • Da esquerda para a direita vs da direita para a esquerda - se quiser inverter as letras, você pode. Se você quiser uma imagem espelhada, defina scale.x=-1 internamente.

  • O estilo do texto não adicionará mais argumentos ao sombreador. Isso vai mudar a geometria. Agora, armazenar o glifo como uma geometria leva _menos_ memória do que armazená-lo em uma textura.

  • Os arquivos de fonte obtidos de APIs devem ser armazenados em cache na máquina do usuário.

Você é muito ingênuo:

  1. Deve ser um batcher externo, porque glifos requerem atributos extras, como âncoras de Bezier, valores de sombra, âncoras de gradiente ..., ou todos devem ser passados ​​como uniformes - tchau, lote.
  2. direita para esquerda e esquerda para direita, e sua mixagem é um problema muito complexo.
    Problemas abertos na biblioteca de renderização de PDF:
    https://github.com/asciidoctor/asciidoctor-pdf/issues/175
  3. Problema em hindi (e linguagem semelhante)
    https://docs.microsoft.com/en-us/typography/opentype/spec/gsub

Não podemos usar dependências externas, pois haverá o mesmo problema de isMobile ou resource-loader .
O núcleo deve estar limpo. Dependências externas mínimas.

@eXponenta

  • O layout misto (ltr e rtl combinados) é menos prioritário do que o alto desempenho. Você sempre pode recorrer ao algoritmo atual. Semelhante para Devanagari. Recursos especiais, como paradas de gradiente, também precisarão de um substituto.

  • O batching pode funcionar aqui porque será mais fácil sem quaisquer texturas. Pode criar um plugin como CanvasGraphicsRenderer .

OK. Você venceu. Faça. Mas fora do núcleo, então iremos examiná-lo e decidir o que devemos fazer então.
Mas sem dependências pesadas.

@eXponenta, por favor, pare de ser tão hostil. Estamos explorando abordagens, mas nada é sólido ainda. Por favor, seja construtivo e não use insultos.

@eXponenta Tem certeza de que oferecemos suporte para texto da direita para a esquerda agora?

Outra abordagem que estamos pensando é renderizar cada glifo usando canvas 2D (o mesmo de agora) separadamente e armazená-los em um atlas. O atlas pode ser reutilizado globalmente para todas as instâncias de PIXI.Text . Cada combinação de glifo + tamanho da fonte + estilo da fonte será armazenada em cache separadamente.

O que você acha de aceitar isso no núcleo?

Ei, pessoal, é realmente emocionante ouvir essas novas abordagens sobre a mesa. @eXponenta , ecoando @bigtimebuddy , vamos mantê-lo construtivo, por favor. Você claramente tem uma boa visão sobre a nova abordagem proposta, mas vamos tentar ver se podemos mitigar os problemas levantados e mantê-la amigável :)

No texto, aqui está meus 2 centavos (pence?) ..

Pessoalmente, acho que a abordagem mais valiosa é obter a velocidade e o desempenho do texto de bitmap, mas sem a necessidade de criar uma fonte de bitmap de antemão. Construção de textura de bitmap dinâmica, se você quiser!

Fontes de bitmap dinâmicas

prós

  • Ferramentas para criar fontes de bitmap são poucas e raras! Adoraria remover essa barreira.
  • poderíamos criar vários tamanhos de texturas de tamanho de texto.
  • Funciona com o Canvas
  • O desempenho de cada glifo pode ser agrupado
  • muitos efeitos legais!
  • API precisaria apenas de um pequeno ajuste:
const textStyle = new TextStyle({
    font:'comic sans',
    fill:'bright green'
});


const bitmapFont = new BitmapFont(textStyle);

contras

  • todos os outros contras das fontes de bitmap atuais de bitmap: P
  • atualizar dinamicamente o atlas de texturas causaria lentidão?

    • Embora isso se estabilize depois de um tempo e talvez possa ser armazenado em cache entre as sessões?

    • O Texto Atual também precisa fazer upload de uma textura cada vez que muda de quadro, essa abordagem faria menos?

Ideias

  • A textura do cache pode ser gerada em um armazenamento local? para visitas subsequentes?
  • Adicione apenas os caracteres usados, para que as texturas sejam tão grandes quanto precisam ser. Atualize conforme avançamos
  • Asse em filtros para esta textura para efeitos extras legais (contorno, sombra etc)
  • podemos explorar reduzindo a memória da fonte de bitmap, não tornando cada glifo um sprite?

SDF
O SDF pode ser uma extensão da abordagem acima? Quão complicado seria gerar essa textura dinamicamente? Seria super janky? Estamos falando de aumentar a base de código em uma grande quantidade?

A maioria das pessoas não tem ideia do que é, então, idealmente, deveria ser escondido. Se precisarmos gerar externamente os campos SDF ou o código necessário para gerar for muito grande ou complexo, isso seria melhor em casa como um plugin com certeza:

const textStyle = new TextStyle({
    font:'comic sans',
    fill:'bright green',
    sdf:true, <----- how sweet would this be, maybe rename to more user friendly like 'cleanEdges'
});

const sdfFont = new BitmapFont(textStyle);

Renderização exata da curva de Bézier

Esta é uma abordagem interessante com certeza! Eu já havia examinado isso no passado e decidido que os switches de sombreador introduzidos no loop de renderização e o mosaico extra necessário teriam uma grande sobrecarga. @SukantPal , você está mais perto dessa ideia do que eu agora, então gostaria de

Dito isso, talvez valha a pena construir um protótipo que possamos testar e bater um pouco para validar nosso pensamento?

Momentos empolgantes para texto: D

O SDF pode ser uma extensão da abordagem acima? Quão complicado seria gerar essa textura dinamicamente? Seria super janky? Estamos falando de aumentar a base de código em uma grande quantidade?

A geração de SDF é cara e há um tempo de empacotamento (quando encontramos diferentes fontes no atlass), mas pode ser armazenado em cache no cliente (como todas as outras variantes).
Há uma implementação de tela (hehe) disso:
https://github.com/mapbox/tiny-sdf
Mas a compilação do SDF não está totalmente correta (alteração do glifo ao aumentar / diminuir o tamanho da fonte)
Parece útil, mas para MSDF devemos executá-lo 3 vezes (por canal).

Nossa, isso é um pequeno código! Parece maduro para um shader também!

@GoodBoyDigital O pacote tiny-sdf gera um campo de distância de canal único. Não produz cantos nítidos quando você armazena o texto em cache em tamanhos de fonte menores. Isso pode ser atenuado armazenando em cache o mesmo tamanho de fonte que está sendo processado.

A geração de SDF de canal único não deve ser tão ruim. O SDF multicanal é superior, exceto que é muito complicado. Eu não conseguia entender como Chumskly estava codificando cantos.

Agora, tudo o que estamos considerando não suportará os layouts rtl e Ltr misturados (como @eXponenta apontou corretamente). Mas não acho que estamos oficialmente apoiando isso agora também.

A abordagem exata tem dois benefícios - tamanho de cache pequeno (você está armazenando vértices e não todos os pixels em cache), anti-serrilhamento mesmo quando a configuração “anti-serrilhamento” está desligada (o texto deve ter anti-serrilhamento sempre, eu acho. A implementação atual faz isso usando canvas 2D).

Quanto à demonstração, você viu minha demonstração quadrática da curva de Bézier? https://codepen.io/sukantpal/pen/GRJawBg?editors=0010

——
Outra coisa que eu e @bigtimebuddy discutimos como a solução mais simples era continuar usando canvas 2D e armazenar em cache os glifos em uma textura. Os glifos podem ser renderizados como um quad. O atlas de cache pode ser reutilizado entre textos. Claro, isso significa que um texto grande está sendo armazenado em cache no mesmo tamanho e, portanto, um grande uso de memória quando muito texto está sendo renderizado, mas apenas uma parte dele (como a página atual em PDF) está visível.

Agradável,

Sim, não se preocupe com rtl ou ltr que sempre estarão disponíveis por meio do método atual e é um problema que todas as técnicas de renderização de texto customizado precisarão ser resolvidas posteriormente.
Essas decisões. deve, idealmente, focar no consumo de memória e no desempenho do tempo de execução.

A demo de Bezier é legal, mas não acho que seja o suficiente para entender as verdadeiras complexidades que podem estar se escondendo se rendermos uma frase completa.

O que escrevi sobre fontes de bitmap dinâmicas acima é exatamente o que você mencionou sobre armazenamento em cache de glifo. Acho que é definitivamente um caminho útil para descer!

Resumindo:

As soluções da curva de Bézier: esse poderia ser um bom caminho, mas acho que requer mais P&D para descobrir como ele se encaixa em casos de uso de produção reais.

Fontes de bitmap dinâmicas de fontes normais: Isso definitivamente devemos explorar, pois sabemos que isso dará um bom desempenho ao mesmo tempo em que mantém a API simples. Meu favorito: D

SDF de canal único Agradeço que não seja um SDF multicanal tão bom, mas a demo me pareceu muito boa! Se o texto pode escalar bem e podemos ter uma textura por fonte, então acho que vale a pena investigar esta rota também!

@GoodBoyDigital Acho que a demonstração tem um texto muito bom. Mas existem diferenças visíveis se você tentar procurar detalhes:

Screen Shot 2020-04-06 at 1 34 29 PM

Screen Shot 2020-04-06 at 1 34 35 PM

O texto na parte superior não é nítido - as bordas têm ondulações. Cada combinação de fonte + (~ 12-15px diferenças no tamanho da fonte) pode precisar ser armazenada em cache separadamente.

Eu estou totalmente bem em ir para SDF se esses wiggles não forem importantes ou se pudermos usar o cache em vários tamanhos de fonte.


A solução mais simples também serve para mim 💯tbh!

Olá a todos.

Sou um super leigo em todas as coisas do Pixi, mas sou um usuário do GDevelop que usa o Pixi como back-end, e venho me aprofundando nisso há um tempo para a implementação do PixiJS e renderização de texto do GDevelop. É realmente impactante para um jogo no qual estou trabalhando e que tem muito texto. (Você pode ver minha pesquisa / exemplos no problema que postei no GDevelop: https://github.com/4ian/GDevelop/issues/1449)

Uma das opções que encontrei concentrava-se em ignorar completamente os problemas de dimensionamento do texto e eliminá-los completamente, sempre deixando a escala do texto em 100%, mas dimensionando o tamanho da fonte do texto em vez disso? Isso é independente de fonte e parece funcionar em todos os tamanhos.

Aqui está um exemplo de código em que isso está sendo feito usando Pixi: https://codepen.io/Tazy/pen/wJVExB
Eu o encontrei neste tópico: https://www.html5gamedevs.com/topic/29576-scalable-text-for-pixijs/

Na verdade, tenho uma recompensa por esse problema, pois esperava que alguém pudesse apenas modificar a extensão Pixi Text para GDevelop, mas (sendo leigo que sou) não percebi que esse era um problema muito maior com o próprio Pixi.

Não tenho ideia se existem problemas potenciais com este método, mas parece evitar todos os problemas de desempenho de sdf / msdf / etc.

@ Silver-Streak Se você quer dizer aumentar o tamanho da fonte pela escala aplicada no objeto de exibição de texto, como isso seria melhor em termos de desempenho? O material SDF ajuda no desempenho porque você não renderiza novamente em tamanhos de fonte maiores.

@ Silver-Streak Claro, sua proposta vai melhorar a _qualidade_, mas não vejo como isso vai melhorar o desempenho.

@ Silver-Streak Claro, sua proposta vai melhorar a _qualidade_, mas não vejo como isso vai melhorar o desempenho.

A menos que eu esteja entendendo mal, mudar o tamanho da fonte não usaria menos recursos do que dimensionar o objeto inteiro ou carregar em outro renderizador? Mais uma vez, sou um super leigo quando se trata de como o dimensionamento do Pixi realmente funciona, apenas fez sentido para mim que deixar a fonte na resolução nativa, mas apenas alterar o tamanho do texto, seria mais rápido do que dimensioná-lo. Se estiver incorreto, desculpe a distração.

@ Silver-Streak Scaling é implementado como uma transformação. Você tem uma matriz de transformação - e alterar a escala nessa matriz é uma operação trivial.

A partir de agora, o texto é renderizado usando Canvas 2D API em uma tela. Em seguida, ele é copiado para a tela. Mudar a escala mudará apenas as coordenadas da tela para as quais o texto na tela está mapeado.

Ahh, isso faz sentido. Isso é lamentável, embora, para ser justo, meu problema seja mais com a renderização gráfica de fontes que fica embaçada depois de ser dimensionada, mas achei que seria um desempenho melhor também.

Embora eu esteja ansioso para que alguém o implemente para corrigir os problemas gerais de qualidade do texto, posso entender perfeitamente o desejo de melhorar o desempenho também.

Obrigado por conversar comigo.

@ Silver-Streak Sem problemas. No entanto, implementar um texto com qualidade em primeiro lugar também não deve ser difícil em WebGL. Alguns aplicativos estão implementando texto criando uma malha por conta própria. Você pode analisar o arquivo de fonte, fazer uma lista de vértices e tesselá-lo. Esses vértices podem ser renderizados por meio de uma malha.

Em escalas altas, isso pode causar pequenas "bordas" - porque, em última análise, você está renderizando o texto como triângulos. Para obter uma qualidade ainda mais alta, você pode usar o sombreador "curva de bezier exata" de que falei neste tópico - ele renderizará as curvas como curvas e não como triângulos.

Se precisarem de ajuda com a qualidade do texto, posso ajudar vocês :)

@SukantPal Definitivamente não sou um contribuidor do GDevelop (nem você gostaria que eu fosse. Sou predominantemente um analista de sistemas de negócios / DevOps na vida, não um desenvolvedor 😄), embora adore esse projeto e seja ativo na comunidade . Também sei que outros membros da comunidade adorariam ver uma solução para a qualidade do texto em escala.

No entanto, se você quiser dar uma olhada no problema no post, também tenho uma recompensa por ele no Bountysource, aberto a todos. Não quero ocupar mais espaço aqui, porém, já que é óbvio que há uma conversa muito mais profunda acontecendo em torno de Pixi em geral, e minha sugestão não foi tão exata quanto eu pensava.

@ Silver-Streak, posso dar uma olhada, já que não tenho nada melhor para fazer na temporada corona

Eu sei que este é um tópico fechado, mas tenho certeza que todos ainda estão procurando a melhor maneira de melhorar a renderização de texto. Eu percebi que alguém tinha uma implementação de msdf que funcionava com Pixi v5. https://github.com/cjsjy123/pixi-msdf-text-v5 Figured Eu mencionaria isso para os interessados.

Este tópico definitivamente deve ser reaberto e mantido ativo - ou pelo menos ter algum tipo de atualização de vez em quando, já que este é um dos problemas mais procurados.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

lucap86 picture lucap86  ·  3Comentários

distinctdan picture distinctdan  ·  3Comentários

gigamesh picture gigamesh  ·  3Comentários

samueller picture samueller  ·  3Comentários

courtneyvigo picture courtneyvigo  ·  3Comentários