Pixi.js: considerada matriz gl?

Criado em 30 jun. 2017  ·  39Comentários  ·  Fonte: pixijs/pixi.js

Isso pode ser heresia, mas vou lançar uma ideia aqui e deixar as pessoas gritarem assassinatos sangrentos ... já consideramos a possibilidade de padronizar em matrizes, pontos que são baseados em array em vez de baseados em objeto? O WebGL espera matrizes na maioria de suas apis de vértice / textura / etc. Também há muitos módulos de ecossistema existentes que padronizam em matrizes antigas ou nativas (por exemplo, https://github.com/toji/gl-matrix) gl-matrix seria bom, visto que muitos navegadores têm suporte de hardware SIMD atrás de sinalizadores , e estarão disponíveis em breve (na verdade, gl-matrix suporta isso internamente agora).

Eu sei que isso seria uma grande mudança de paradigma para pixi, e quebraria totalmente a compatibilidade com versões anteriores ... ainda, queria jogar isso lá fora e ver o que as pessoas pensam e o quanto / pouco elas odeiam a ideia;)

Comentários muito úteis

@GoodBoyDigital Acho que talvez você esteja certo. recorrendo ao meu amigo sra. google, me deparei com isto:

https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript

a resposta mais bem avaliada (atualmente com 44 votos) parece razoável e relevante.

Todos 39 comentários

1) SIMD está morto. https://github.com/rwaldron/tc39-notes/blob/a66df6740eec3358d5e24f81817db99d6ee41401/es8/2017-03/mar-21.md#10if -simdjs-status-update

2) 6 campos simples "a, b, c, d, tx, ty" estão funcionando muito melhor até do que a matriz Float32Array (9). Não consigo dar links para os testes, mas tanto eu quanto

3) JS oferece precisão dupla, o que é crítico para aplicativos que funcionam com grandes coordenadas. "projection x transform" para gráficos e sprites são melhores no lado JS.

Estou usando um Float32Array (9) na v5, que em meus testes de jsperf era semelhante, senão o mesmo perf, e nos impede de precisar fazer operações toArray e transpor.

https://github.com/pixijs/pixi.js/blob/455c059e8d155c1d9a05fc2ece2c02b3bdf8bf84/plugins/core/src/math/Matrix.js

gl-matrix é benéfico porque tinha SIMD (que, como Ivan mencionou, é uma especificação morta), mas também tem falhas em sua implementação. Queremos uma matriz 3x3 ( Float32Array(9) ) para atingir a GPU, mas fazemos as operações como se fosse uma matriz 2D para economizar tempo de computação. gl-matrix não tem um bom mecanismo para isso.

A versão v5 usa armazenamento que podemos carregar diretamente para a GPU, além de operar apenas nas partes 2D de nosso interesse. Ele também nos prepara para usar SharedArrayBuffers e outras otimizações que podem nos permitir trabalhar mais com webworkers. Veremos até onde podemos chegar com isso.

@englercj Temo que teremos que usar Math.fround em muitos lugares para ser consistente. Experimente perf Float64Array.

Eu poderia tentar torná-lo Float64Array, mas ainda precisamos reduzi-lo para precisão única para upload. Lembre-se também de que estamos usando um float32 ao fazer upload para a GPU . Então, fazemos as contas em dobro e depois truncamos para simples. Isso pode ser mais preciso do que fazer tudo sozinho, mas eu gostaria de tentar e ser consistente com o tipo de dados que carregamos. Infelizmente, isso significa precisão única até GL 4.0, e WebGL 2 é apenas GL ES 3.0 :(

O upload da matriz nunca foi nosso gargalo, usamos "uniform3fv" nesses locais, não é uma operação simples, e é seguido por um drawCall e, na maioria dos casos, um grande upload de buffer. O aplicativo pixi pesado está fazendo apenas cerca de 400 chamadas por quadro,

Depois de encontrar problemas do usuário com grandes coordenadas, prefiro armazenar tudo com dupla precisão até fazer o upload.

Além disso, a notação "a, b, c, d, tx, ty" é mais fácil de escrever e ler do que "0,1,3,4,6,7". Também são usados ​​na lombada, além disso possuem transformações muito sofisticadas. Se mudarmos para matrizes, não será tão fácil verificar nosso código mais tarde. Para algumas pessoas, é difícil imaginar operações de matriz, mas eu as leio facilmente.

UPD. Também acho que este nos ajudará mais do que converter matrizes: https://github.com/gameofbombs/gobi/blob/master/src/core/math/FlatTransform2d.ts , que é a transformação "Flat", contém todos os campos necessários para cálculo da matriz.

UPD2. Mas para transformações 3D, Float32Array (16) é melhor e não vou falar contra isso.

Meu voto vai para aquele que tiver melhor desempenho. A última vez que verifiquei, usar objetos em vez de matrizes foi mais rápido. Embora eu não tenha 100% de certeza se isso ainda é verdade, isso pode muito bem ter mudado!

Para 3d, eu prefiro o estilo gl-matrix, principalmente porque as coisas são muito carregadas para a GPU! Com Pixi geralmente não é o caso. A maior parte da manipulação acontece em js land (por exemplo, sprite batcher).

https://jsperf.com/obj-vs-array-view-access/1

Aqui está o teste que fiz. Object é mais lento que Float32Array, sendo que ambos são muito mais lentos que Array normal. Então, obtemos precisão dupla e a velocidade de CPU mais rápida e pode ser carregado diretamente.

Edit: Parece que Array result foi um acaso, não consigo reproduzi-lo?

Meu voto vai para aquele que tiver melhor desempenho.
A última vez que verifiquei, usar objetos em vez de matrizes foi mais rápido.

Isso é muito interessante. Parece contra-intuitivo que os objetos sejam mais rápidos do que um array puramente numérico, porque o vm subjacente deve ser capaz de fazer uma série de otimizações, sabendo que a semântica do objeto pode ser amplamente eliminada. Estou curioso para saber se você tem alguma ideia _por que_ objetos podem ser mais rápidos?

Resultados relevantes do meu comp (i7, Windows 10):

Chrome 59:

image

Firefox 54.0.1 (32 bits):

image

Microsoft Edge 40.15063.0.0:

image

Parece que meu resultado do cromo de Array ser mais rápido foi um acaso, não tenho certeza do que foi, mas não consigo reproduzi-lo agora. É cerca de 50 milhões de ops / s, mas foi de 500 milhões de ops / s em uma execução que fiz antes. Esquisito...

De qualquer maneira, o membro Float32Array tem consistentemente a mesma velocidade ou mais rápido do que o objeto em todos os navegadores. É por isso que eu mudei, é a mesma velocidade de antes (ou mais rápida), mas agora evitamos transpor e preencher um array para uploads.

Você pode fazer isso com Float64 também, por favor? :)

Preocupo-me com a qualidade do código mais do que com mais uma operação de matriz por drawcall.

Além disso, não usamos muito invert. updateTransform () é o nosso problema.

Preocupo-me com a qualidade do código mais do que com mais uma operação de matriz por drawcall.

Como Float64Array tem melhor qualidade de código? Eu entendo que obtemos mais precisão, mas não tenho certeza se entendi por que isso é tão importante, visto que reduzimos à precisão simples de qualquer maneira.

Além disso, não usamos muito invert. updateTransform () é o nosso problema.

Este é o benchmarking da leitura / gravação do armazenamento de dados, a operação realizada nos valores intermediários em irrelevantes.

Preocupo-me com a qualidade do código mais do que com mais uma operação de matriz por drawcall.

Eu concordo com isso também. Considere que "padronizar" em matrizes, pontos e vetores baseados em array pode realmente ser mais uma melhoria na qualidade do código do que no desempenho ... Eu consideraria a reutilização de bibliotecas populares de matriz / vetor js e mais interoperabilidade com módulos de renderização populares para ser uma grande vitória na qualidade do código.

Este é um benchmarking de leitura / gravação do armazenamento de dados,
a operação realizada nos valores intermediários é irrelevante.

Eu acho que vocês dois estão certos. Este é um benchmarking geral de leitura / gravação de armazenamento de dados. Mas Ivan tem um ponto muito bom; se updateTransform() é a operação opressora, seria atraente ver isso.

Eu me preocupo com micro benchmarks em geral; com esses conjuntos de dados estáticos, eu me pergunto se inadvertidamente acabamos aproveitando as otimizações do compilador em javascript vms nesses testes. executar testes do mundo real seria muito mais esclarecedor (ao custo de ser muito mais trabalhoso para configurar).

Eu concordo com isso também. Considere que "padronizar" em matrizes, pontos e vetores baseados em array pode realmente ser mais uma melhoria na qualidade do código do que no desempenho ... Eu consideraria a reutilização de bibliotecas populares de matriz / vetor js e mais interoperabilidade com módulos de renderização populares para ser uma grande vitória na qualidade do código.

Eu sou a favor da matriz de precisão dupla "(a, b), (c, d), (tx, ty)", com conversão para float32array para upload e apoiado por "posX, posY, pivotX, pivotY, scaleX, scaleY, shearX, shearY, rotZ ". Vou usá-lo em meu fork de qualquer maneira, mas prefiro não lidar com matrizes em pixi master também. Esse é o meu padrão.

Também duvido que seja possível fazer as pessoas usarem um ou dois padrões de matemática vec em js.

Como Float64Array tem melhor qualidade de código? Eu entendo que obtemos mais precisão, mas não tenho certeza se entendi por que isso é tão importante, visto que reduzimos à precisão simples de qualquer maneira.

Multiplicamos a transformação da câmera pela transformação do sprite (para rolagem) em updateTransform. O resultado cabe na tela apenas se for pequeno, então os números são pequenos no final, mas a posição do sprite e a posição da câmera podem ser grandes. Soluções do lado do usuário:

1) computar tudo a seu favor. PIXI lida apenas com coordenadas relativamente pequenas.
2) divida o mundo em pedaços com grandes coords, sprite tem coords relativamente pequenos, entretanto isso não vai funcionar para câmeras de subpixel => câmera precisa de coords grandes e pequenos também.

Não há problema em forçar o usuário a fazer isso do lado dele no caso de um projeto grande, mas para os pequenos é apenas mais uma dor de cabeça.

Também duvido que seja possível fazer as pessoas usarem um ou dois padrões de matemática vec em js.

Eu não entendo o que você quer dizer, você pode elaborar?

Eu não entendo o que você quer dizer, você pode elaborar?

Eu não posso, é demais para mim.

Temos pessoas com experiências diferentes tanto em otimizações de baixo nível. e construções de linguagem, DSL-s. Precisamos de um padrão que satisfaça a todos nós em algum nível.

Nos últimos dois anos, fiz dois garfos de pixi (para v3 e v4) com transformações diferentes, estou lidando com "pixi-spine" que tem sua própria transformação avançada e estou fazendo o terceiro garfo. Do ponto de vista do "passado ivan", array é o melhor porque sua forma mais simples e existe "gl-matrix".

Eu concordo com @ivanpopelyshev que manter 64 bits é importante. Estou tentando descobrir como fazemos isso, de forma eficiente, sem criar e copiar buffers de cada quadro.

Talvez o armazenemos como Float64Array e, ao fazer o upload, copie-o para um Float32Array. Isso pelo menos nos permite usar um array digitado como suporte de armazenamento, que deve ser mais fácil de copiar de / para um webworker.

Float64Array é então, eu só terei que lembrar de usar (0,1), (3,4), (6,7) como X, Y, traduzir

Todas coisas interessantes, pessoal, gostaria que fizéssemos um benchmark de nossa solução proposta em um cenário de pixi real - algo como o bunnymark.

Minha experiência nesta área é que quando eu e @ivanpopelyshev mudamos pela última vez para gl-matrix para a marca do coelho, descobrimos que ele funcionava significativamente mais lento (como um terceiro mais lento!)

Isso foi há pouco tempo e eu realmente preferia que eu estivesse errado aqui!
Se a diferença de velocidade agora for insignificante, então acho que a rota proposta acima será ótima.

Pixi é VELOCIDADE: P Vamos nos certificar de que testamos antes de nos comprometermos totalmente com qualquer solução.

Ainda precisamos de benchmark de Float64Array. Essa implementação é aceitável para mim, mas pessoalmente irei usar a matriz antiga em meu fork. Também não se esqueça de adicionar propriedades apenas para compatibilidade.

Mais uma coisa: @mreinstein , um dos

Há algo em que eu possa ajudar com relação a: esta marca de coelho atualizada? Fico feliz em doar algum tempo com isso.

Eu estaria muito interessado em acompanhar as coisas de desempenho que acontecem aqui. Independentemente dos resultados. Se o material baseado em array acaba sendo muito mais lento, estou realmente curioso sobre o _why_ por trás disso.

Algo a se observar: o teste de desempenho ... o v8 do chrome recentemente (em v59) lançou algumas mudanças bem dramáticas para o v8 chamadas turbofan. Supostamente, ele tem algumas melhorias significativas de desempenho.

https://v8project.blogspot.com/2017/05/launching-ignition-and-turbofan.html

pode ser interessante rodar o bunnymark atualizado em uma versão anterior ao turbofan estar presente vs agora, apenas por diversão.

@GoodBoyDigital Eu atualizei o banco para incluir Float64Array, e é consistentemente mais rápido para ler / gravar no array do que no objeto. Se ficar mais lento em pixi, então algo mais mudou, porque ler / gravar em um armazenamento de apoio Float64Array é mais rápido do que um objeto.

https://jsperf.com/obj-vs-array-view-access/1
image

Apenas no Edge a velocidade do objeto corresponde / excede Float64Array, e é muito próxima.

Eu testei isso no meu env e obtive resultados semelhantes ... que diabos ?! Por que a leitura / gravação de array Float64 seria significativamente mais rápida do que as operações de array Float32 equivalentes? A única coisa que vem à mente é talvez os flutuadores de 64 bits se alinhem com os limites das palavras. Estou perplexo.

Obrigado pela oferta @mreinstein ! Se você pudesse nos ajudar com alguns testes de desempenho, isso certamente colocaria todo o debate para a cama com fatos concretos e frios!

A melhor coisa a fazer é fork pixi e então substituir as transformações por gl-matrix ou classe @englercj matrix. Neste caso, só precisamos fazer o lote do sprite funcionar também - não o motor inteiro!

Então, quando tivermos uma versão ajustada, podemos testar o desempenho aqui: https://pixijs.github.io/bunny-mark/
Podemos mexer com os diferentes tipos de arrays.

@englercj isso é cara incrível! É encorajador ver esses resultados com certeza. A versão v5 está perto de um estado em que podemos dar uma chance a Bunny Mark?

@mreinstein apenas um palpite, acho que pode ter a ver com a conversão de 64 bits -> 32 bits
como um número em js é 64 bits certo?

@GoodBoyDigital Acho que talvez você esteja certo. recorrendo ao meu amigo sra. google, me deparei com isto:

https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript

a resposta mais bem avaliada (atualmente com 44 votos) parece razoável e relevante.

Se você pudesse nos ajudar com alguns testes de desempenho

Tudo bem, fico feliz em ajudar com isso. :)

A melhor coisa a fazer é fork pixi

@GoodBoyDigital @englercj qual é o melhor branch a partir deste ponto?

em seguida, substitua as transformações por gl-matrix ou classe @englercj matrix.
Neste caso, só precisamos fazer o lote do sprite funcionar também - não o motor inteiro!

Você pode elaborar um pouco mais sobre isso? Eu não quero converter todo o motor para gl-matrix apenas para rodar o perf bench ... Não vejo nenhum sprite batch batch em https://github.com/pixijs/bunny-mark Parece haver um PIXI.Container como o elemento raiz, ao qual os coelhos são adicionados. Você está dizendo que eu deveria começar com pixi.container, pixi.sprite e voltar a partir daí para cima na árvore de dependências para encontrar todos os lugares onde o material de transformação precisa ser substituído? Não estou dizendo que discordo, apenas quero ter certeza de que tenho a estratégia certa para minimizar o trabalho desnecessário.

updateTransform tem duas operações de matriz dentro:

  1. composição de posição, pivô, escala, rotação - não pode ser melhorada.
  2. multiplicação pela matriz parental - pode ser melhorada.

Eu sugiro fazer uma classe de matriz com antigos adereços "a, b, c, d, tx, ty" apoiados por Float64Array e reescrever updateTransform. Além disso, @mreinstein fez coisas suficientes para estar no núcleo, sugiro que o adicionemos, para que ele tenha acesso a branches e build farm.

então ele tem acesso a galhos e construir fazenda.

Todo mundo tem acesso a isso, o fork + PR faz tudo isso.

Estou curioso para saber que tipo de resultados vocês obtêm no bunny mark ... quando tento vários branches (dev, release, outros) do cromo com o padrão de 100k coelhos eu obtenho 10-16fps consistentemente. Fazendo uma análise de desempenho no código executado por mais de 8.75s:

screen shot 2017-07-02 at 11 58 03 am

Quase todo o tempo é gasto em chamadas de javascript.

screen shot 2017-07-02 at 11 57 50 am

Desse tempo gasto em javascript, a maior parte do tempo é gasto em Sprite.calculateVertices() . 4x mais do que é gasto em TransformStatic.updateTransform()

No firefox obtenho cerca de duas vezes a taxa de quadros, mas a divisão do tempo gasto é semelhante; calculateVertices() ocupa a maior parte do tempo em ambos os navegadores. Isso é esperado? Você está obtendo resultados semelhantes em suas execuções de marca de coelho?

Isso é esperado para coelhos. E as operações de matriz das quais estamos falando são realizadas em dois lugares: upateTransform () para multiplicação e flush () ONCE PER CALL. Não importa em grande escala.

Estou confuso. :( Como posso saber o desempenho desse benchmark? Tive a impressão de que é principalmente baseado em fps puro alcançado. Se isso for verdade, então minha taxa de quadros está sendo _severamente_ limitada pelos cálculos dos vértices. Olhando para o primeiro diagrama que colei acima, a renderização é uma pequena fração do tempo total do quadro. As atualizações de vértice e transformação estão sobrecarregando o tempo total do quadro.

Se isso for falso e o fps não for a medida do desempenho de minha construção de pixi, que critérios devo usar para medir uma determinada construção?

Ele pode ser regulado na CPU ou no lado da GPU (não é mostrado),

Se você tem FPS muito menor que 60, mas "inativo" é grande - sua GPU.

Se ocioso for pequeno, então sua CPU.

CalculeVertices tem operação de matriz dentro - apenas multiplicação de quatro cantos por matriz.

Apenas uma pequena ideia para bifurcação de TS: adicionar transformação que inline propriedades para tipos de matriz. É uma pena que ainda não haja esse tipo de transformações para TS :(

Eu acho que isso pode ser feito para o babel também se encontrarmos uma maneira de anotar as variáveis ​​da matriz.

Não consigo mais entender o que está acontecendo.

Alt text

Algo para se ter em mente: o objetivo deste benchmark em primeiro lugar era ver se usar objetos versus gl-matrix seria um grande impacto no desempenho, como se pensava. Independentemente das diferenças em nossos resultados de benchmark, parecemos mostrar consistentemente que o desempenho do objeto não é pior do que a maioria dos casos de array.

Também quero evitar tirar conclusões sobre o desempenho das pixi em geral aqui, porque fiz o perfil da marca do coelho. No Chrome,> 50% do tempo total do programa é gasto em Sprite.calculateVertices() (40% ish), seguido por TransformStatic.updateTransform() (11% ish), o Firefox parece rodar duas vezes mais rápido, mas a proporção de tempo gasto nessas 2 funções ainda é consistente.

Quero evitar ir muito longe do tópico, mas direi que, ao fazer este perfil de bunnymark, estou começando a pensar que nosso uso de getters / setters es5 pode ter algo a ver com a queda de perf em chrome: https: //jsperf.com/es5-getters-setters-versus-getter-setter-methods/10

Algum de vocês está relaxando aqui? Acho que seria mais fácil bater um papo em tempo semi-real do que passar mensagens aqui.

Qual é o seu e-mail para @mreinstein ? Vou convidá-lo a relaxar 👍

Com base no que @englercj diz, aparentemente não estamos fazendo isso. Fechando.

Apenas uma pequena atualização: tenho uma matriz 3x3 em vez de 3x2 em https://github.com/pixijs/pixi-projection/blob/master/src/Matrix2d.ts , com base em Float64Array.

Este tópico foi bloqueado automaticamente, pois não houve nenhuma atividade recente depois que ele foi fechado. Abra um novo problema para bugs relacionados.

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

Questões relacionadas

YuryKuvetski picture YuryKuvetski  ·  3Comentários

Makio64 picture Makio64  ·  3Comentários

neciszhang picture neciszhang  ·  3Comentários

finscn picture finscn  ·  3Comentários

gigamesh picture gigamesh  ·  3Comentários