Pixi.js: considéré comme gl-matrice ?

Créé le 30 juin 2017  ·  39Commentaires  ·  Source: pixijs/pixi.js

C'est peut-être une hérésie, mais je vais lancer une idée ici et laisser les gens crier au meurtre... avons-nous déjà envisagé la possibilité de standardiser sur des matrices, des points basés sur des tableaux plutôt que sur des objets ? WebGL attend des tableaux dans la plupart de ses apis vertex/texture/etc. Il existe également de nombreux modules d'écosystème existants qui se standardisent sur des tableaux anciens ou natifs (par exemple, https://github.com/toji/gl-matrix). , et seront bientôt disponibles (en fait, gl-matrix le supporte en interne maintenant.)

Je me rends compte que ce serait un changement de paradigme massif pour pixi, et briserait totalement la rétrocompatibilité... encore, je voulais jeter ça là-bas et voir ce que les gens pensent, et combien/peu ils détestent l'idée ;)

Commentaire le plus utile

@GoodBoyDigital Je pense que vous avez peut-être raison. recourir à mon amie mrs. google, je suis tombé sur ça :

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

la réponse la mieux notée (actuellement à 44 voix) semble raisonnable et pertinente.

Tous les 39 commentaires

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

2) Les 6 champs simples "a,b,c,d,tx,ty" fonctionnent bien mieux même que la matrice Float32Array(9). Je ne peux pas donner de liens vers les tests, mais moi et @GoodBoyDigital avons

3) JS fait une double précision, ce qui est essentiel pour les applications qui fonctionnent avec de grandes coordonnées. "projection x transform" pour les graphiques et les sprites sont mieux du côté JS.

J'utilise un Float32Array(9) en v5 qui dans mes tests jsperf était similaire sinon la même performance, et nous évite d'avoir à faire des opérations toArray et transposer.

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

gl-matrix est bénéfique car il avait SIMD (qui, comme Ivan l'a mentionné, est une spécification morte), mais présente également des inconvénients dans leur implémentation. Nous voulons qu'une matrice 3x3 ( Float32Array(9) ) atteigne le GPU, mais effectuons les opérations comme s'il s'agissait d'une matrice 2D pour gagner du temps de calcul. gl-matrix n'a pas de bon mécanisme pour ça.

La version v5 utilise un stockage que nous pouvons télécharger directement sur le GPU et ne fonctionne que sur les pièces 2D qui nous intéressent. Cela nous permet également d'utiliser SharedArrayBuffers et d'autres optimisations qui pourraient nous permettre de travailler davantage sur les webworkers. On verra jusqu'où on peut aller.

@englercj Je crains que nous devions utiliser Math.fround à de nombreux endroits pour être cohérent. Essayez perf Float64Array.

Je pourrais essayer de le faire Float64Array, mais nous devons encore le réduire en simple précision pour le téléchargement. Gardez également à l'esprit que nous utilisons actuellement un float32 lorsque nous téléchargeons sur le GPU . Nous faisons donc le calcul en double, puis tronquons en simple. C'est peut-être plus précis que de tout faire en un seul, mais j'aimerais essayer d'être cohérent avec le type de données que nous téléchargeons. Malheureusement, cela signifie une simple précision jusqu'à GL 4.0, et WebGL 2 n'est que GL ES 3.0 :(

Le téléchargement de matrice n'a jamais été notre goulot d'étranglement, nous utilisons "uniform3fv" à ces endroits, ce n'est pas une opération simple, et il est suivi d'un drawCall, et dans la plupart des cas, d'un gros téléchargement de tampon. L'application Heavy pixi ne fait qu'environ 400 appels par image,

Après avoir rencontré des problèmes d'utilisateur avec de grosses coordonnées, je préfère tout stocker avec une double précision jusqu'à ce que nous le téléchargeions.

De plus, la notation "a,b,c,d,tx,ty" est plus facile à écrire et à lire que "0,1,3,4,6,7". Il est également utilisé dans la colonne vertébrale, ils ont en plus des transformations très sophistiquées. Si nous passons aux matrices, il ne sera pas si facile de vérifier notre code plus tard. Pour certaines personnes, il est difficile d'imaginer des opérations matricielles, mais je les lis facilement.

UPD. Je pense aussi que celui-ci nous aidera plus que de convertir des matrices : https://github.com/gameofbombs/gobi/blob/master/src/core/math/FlatTransform2d.ts , c'est la transformation "Flat", contient tous les champs nécessaires pour le calcul de la matrice.

UPD2. Mais pour les transformations 3D, Float32Array(16) est meilleur et je ne m'y opposerai pas.

Mon vote va à celui qui donne les meilleures performances. La dernière fois que je l'ai vérifié, l'utilisation d'objets sur des tableaux était plus rapide. Bien que je ne sois pas sûr à 100% si c'est toujours vrai, cela a peut-être changé !

Pour la 3D, je privilégie le style gl-matrix, principalement parce que les choses sont souvent téléchargées sur le GPU ! Avec Pixi, ce n'est généralement pas le cas. La plupart des manipulations se produisent dans js land (par exemple, sprite batcher).

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

Voici les tests que j'ai fait. L'objet est plus lent que Float32Array, les deux étant bien plus lents que le tableau normal. Ensuite, nous obtenons une double précision et la vitesse de processeur la plus rapide et il peut être directement téléchargé.

Edit : On dirait que le résultat de Array était un coup de chance, je ne peux pas le reproduire ?

Mon vote va à celui qui donne les meilleures performances.
La dernière fois que je l'ai vérifié, l'utilisation d'objets sur des tableaux était plus rapide.

C'est très intéressant. Il semble contre-intuitif que les objets soient plus rapides qu'un tableau purement numérique, car la machine virtuelle sous-jacente devrait être capable de faire un certain nombre d'optimisations, sachant que la sémantique des objets pourrait être largement supprimée. Je suis curieux de savoir si vous avez des idées _pourquoi_ les objets pourraient être plus rapides ?

Résultats pertinents de ma composition (i7, Windows 10) :

Chrome 59 :

image

Firefox 54.0.1 (32 bits) :

image

Microsoft Edge 40.15063.0.0 :

image

On dirait que mon résultat chrome de Array étant plus rapide était un coup de chance, je ne sais pas ce que c'était mais je ne peux pas le reproduire maintenant. Il s'agit d'environ 50 millions d'opérations/s, mais 500 millions d'opérations/s sur une exécution que j'ai effectuée auparavant. Bizarre...

Dans tous les cas, le membre Float32Array est systématiquement à la même vitesse ou plus rapide que l'objet dans tous les navigateurs. C'est pourquoi je l'ai basculé, c'est la même vitesse qu'avant (ou plus rapide) mais on évite désormais de transposer et de remplir un tableau pour les uploads.

Pouvez-vous également faire cela avec Float64, s'il vous plaît ? :)

Je me soucie plus de la qualité du code que d'une opération matricielle supplémentaire par drawcall.

De plus, nous n'utilisons pas beaucoup l'inversion. updateTransform() est notre problème.

Je me soucie plus de la qualité du code que d'une opération matricielle supplémentaire par drawcall.

En quoi Float64Array est-il de meilleure qualité pour le code ? Je comprends que nous obtenons plus de précision, mais je ne suis pas sûr de comprendre pourquoi c'est si important étant donné que nous réduisons de toute façon à une simple précision.

De plus, nous n'utilisons pas beaucoup d'inversion. updateTransform() est notre problème.

Il s'agit d'évaluer la lecture/écriture du stockage de données, l'opération effectuée sur les valeurs intermédiaires n'est pas pertinente.

Je me soucie plus de la qualité du code que d'une opération matricielle supplémentaire par drawcall.

Je suis d'accord avec ça aussi. Considérez que la "normalisation" sur des matrices, des points et des vecteurs basés sur des tableaux pourrait en fait être davantage une amélioration de la qualité du code qu'une amélioration des performances... J'envisagerais la réutilisation des bibliothèques de matrices/vecteurs js populaires et une plus grande interopérabilité avec les modules de rendu populaires pour être une grande victoire de qualité de code.

Il s'agit d'évaluer la lecture/écriture du stockage de données,
l'opération effectuée sur les valeurs intermédiaires n'est pas pertinente.

Je pense que vous avez raison tous les deux. Il s'agit d'une analyse comparative générale du stockage de données en lecture/écriture. Mais Ivan a un très bon point ; si updateTransform() est l'opération écrasante, il serait convaincant de le voir.

Je m'inquiète des micro-benchmarks en général ; avec ces ensembles de données statiques, je me demande si nous finissons par profiter par inadvertance des optimisations du compilateur dans javascript vms dans ces tests. exécuter des tests dans le monde réel serait beaucoup plus instructif (au prix d'un travail de configuration beaucoup plus important.)

Je suis d'accord avec ça aussi. Considérez que la "normalisation" sur des matrices, des points et des vecteurs basés sur des tableaux pourrait en fait être davantage une amélioration de la qualité du code qu'une amélioration des performances... J'envisagerais la réutilisation des bibliothèques de matrices/vecteurs js populaires et une plus grande interopérabilité avec les modules de rendu populaires pour être une grande victoire de qualité de code.

Je suis pour la matrice double précision "(a,b),(c,d),(tx,ty)", avec conversion en float32array pour le téléchargement et soutenu par "posX,posY,pivotX, pivotY,scaleX,scaleY, cisaillementX,cisaillementY,rotZ". Je vais quand même l'utiliser dans mon fork, mais je préfère ne pas trop m'occuper des tableaux matriciels dans master pixi. C'est ma norme.

Je doute également qu'il soit possible de faire en sorte que les gens utilisent un ou deux standards pour les maths vec en js.

En quoi Float64Array est-il de meilleure qualité pour le code ? Je comprends que nous obtenons plus de précision, mais je ne suis pas sûr de comprendre pourquoi c'est si important étant donné que nous réduisons de toute façon à une simple précision.

Nous multiplions la transformation de la caméra par la transformation du sprite (pour le défilement) dans updateTransform. Le résultat ne s'affiche à l'écran que s'il est petit, donc les nombres sont petits à la fin, mais la position du sprite et la position de la caméra peuvent être grandes. Solutions côté utilisateur :

1) tout calculer de son côté. PIXI ne traite que des coordonnées relativement petites.
2) diviser le monde en morceaux avec de grandes coordonnées, le sprite a des coordonnées relativement petites, mais cela ne fonctionnera pas pour la caméra subpixel => la caméra a également besoin de grandes et petites coordonnées.

C'est bien de forcer l'utilisateur à le faire de son côté en cas de gros projet, mais pour les petits, c'est juste un casse-tête de plus.

Je doute également qu'il soit possible de faire en sorte que les gens utilisent un ou deux standards pour les maths vec en js.

Je ne comprends pas ce que tu veux dire, peux-tu préciser ?

Je ne comprends pas ce que tu veux dire, peux-tu préciser ?

Je ne peux pas, c'est trop pour moi.

Nous avons des personnes avec des expériences différentes à la fois dans les optimisations de bas niveau. et les constructions de langage, DSL-s. Nous avons besoin d'un standart qui nous satisfasse tous à un certain niveau.

Au cours des deux dernières années, j'ai fait deux forks de pixi (pour v3 et v4) avec différentes transformations, je m'occupe de "pixi-spine" qui a sa propre transformation avancée et je fais un troisième fork. Du point de vue du "passé d'ivan", le tableau est le meilleur car sa forme la plus simple et il y a "gl-matrix".

Je suis d'accord avec @ivanpopelyshev que le maintien de 64 bits est important. J'essaie de comprendre comment nous le faisons, efficacement, sans créer et copier des tampons à chaque image.

Peut-être que nous le stockons en tant que Float64Array et, lors du téléchargement, le copions dans un Float32Array. Cela nous permet au moins d'utiliser un tableau typé comme support de stockage qui devrait être plus facile à copier vers/depuis un webworker.

Float64Array c'est alors, je vais juste devoir me souvenir d'utiliser (0,1), (3,4), (6,7) comme X,Y,traduire

Tous les gars intéressants, j'aimerais que nous comparions notre solution proposée dans un vrai scénario de pixi - quelque chose comme bunnymark.

Mon expérience dans ce domaine est que lorsque @ivanpopelyshev et moi

C'était il y a peu de temps et je préférerais vraiment que je me trompe ici !
Si la différence de vitesse est maintenant négligeable, je pense que l'itinéraire proposé ci-dessus sera un as.

Pixi est SPEEED :P Assurons-nous de tester avant de nous engager pleinement dans une solution.

Nous avons encore besoin de benchmark de Float64Array. Cette implémentation est acceptable pour moi, mais personnellement j'utiliserai l'ancienne matrice dans mon fork. N'oubliez pas non plus d'ajouter des propriétés juste pour la compatibilité.

Encore une chose:

Y a-t-il quelque chose que je puisse aider concernant ce bunnymark mis à jour ? Je suis heureux de donner un peu de temps avec ça.

Je serais très intéressé à suivre les trucs de perf qui se passent ici. Peu importe les résultats. Si les éléments basés sur les tableaux finissent par être beaucoup plus lents, je suis vraiment curieux de savoir le _pourquoi_ qui se cache derrière.

Quelque chose à noter concernant les tests de performances... La v8 de Chrome a récemment (en v59) publié des changements assez spectaculaires pour la v8 appelés turbofan. Soi-disant, il a des améliorations de performances significatives.

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

pourrait être intéressant d'exécuter le bunnymark mis à jour sur une version avant que le turboréacteur ne soit présent par rapport à maintenant, juste pour le plaisir.

@GoodBoyDigital J'ai mis à jour le banc pour inclure Float64Array, et il est toujours plus rapide de lire/écrire dans le tableau que l'objet. Si cela devient plus lent dans pixi, alors quelque chose d'autre a changé, car la lecture/écriture dans un magasin de sauvegarde Float64Array est plus rapide qu'un objet.

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

Ce n'est que sur Edge que la vitesse de l'objet correspond/dépasse Float64Array, et elle est vraiment proche.

J'ai testé cela dans mon environnement et j'obtiens des résultats similaires... qu'est-ce que c'est ?! Pourquoi la lecture/écriture du tableau Float64 serait-elle beaucoup plus rapide que les opérations équivalentes du tableau Float32 ? La seule chose qui me vient à l'esprit est peut-être que les flottants 64 bits s'alignent sur les limites des mots ? Je suis perplexe.

Merci pour l'offre @mreinstein ! Si vous pouviez nous aider avec quelques tests de performances, cela mettrait certainement tout le débat au lit avec des faits bruts et froids !

La meilleure chose à faire est de fork pixi, puis de remplacer les transformations par gl-matrix ou @englercj matrix class. Pour cet exemple, nous n'avons vraiment besoin que de faire fonctionner le lot de sprites - pas le moteur entier !

Ensuite, une fois que nous aurons une version modifiée, nous pouvons tester les performances ici : https://pixijs.github.io/bunny-mark/
Nous pouvons déconner avec les différents types de tableaux.

@englercj c'est génial mec! C'est encourageant de voir ces résultats à coup sûr. La version v5 est-elle proche d'un état où nous pouvons donner un tour à Bunny Mark ?

@mreinstein juste une intuition, je pense que cela peut être
en tant que nombre en js, c'est 64 bits, n'est-ce pas ?

@GoodBoyDigital Je pense que vous avez peut-être raison. recourir à mon amie mrs. google, je suis tombé sur ça :

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

la réponse la mieux notée (actuellement à 44 voix) semble raisonnable et pertinente.

Si vous pouviez nous aider avec des tests de perf

Très bien, heureux de vous aider. :)

La meilleure chose à faire est de fork pixi

@GoodBoyDigital @englercj quelle est la meilleure branche à partir de laquelle bifurquer à ce stade ?

puis remplacez les transformations par la classe matricielle gl-matrix ou @englercj .
Pour cet exemple, nous n'avons vraiment besoin que de faire fonctionner le lot de sprites - pas le moteur entier !

Pouvez-vous développer un peu plus cela? Je ne veux pas convertir l'ensemble du moteur en gl-matrix juste pour exécuter le banc de perf... Je ne vois aucun truc par lots de sprite dans https://github.com/pixijs/bunny-mark Il semble y avoir un PIXI.Container comme élément racine, auquel les lapins sont ajoutés. Êtes-vous en train de dire que je devrais commencer par pixi.container, pixi.sprite et remonter à partir de là dans l'arborescence des dépendances pour trouver tous les endroits où les éléments de transformation doivent être remplacés ? Je ne dis pas que je suis en désaccord, je veux juste m'assurer que j'ai la bonne stratégie pour minimiser le travail inutile.

updateTransform contient deux opérations matricielles :

  1. composition à partir de la position, du pivot, de l'échelle, de la rotation - ne peut pas être améliorée.
  2. multiplication par matrice parente - peut être améliorée.

Je suggère de créer une classe matricielle avec les anciens accessoires "a,b,c,d,tx,ty" soutenus par Float64Array et de réécrire updateTransform. De plus, @mreinstein a fait suffisamment de choses pour être dans le noyau, je suggère que nous l'ajoutions, afin qu'il ait accès aux branches et construise une ferme.

il a donc accès aux succursales et construit une ferme.

Tout le monde y a accès, fork + PR fait tout ça.

Je suis curieux de savoir quels types de résultats vous obtenez dans Bunny Mark. Lorsque j'essaie diverses branches (dev, release, autres) de Chrome avec les 100 000 lapins par défaut, j'obtiens systématiquement 10 à 16 images par seconde. Faire une analyse des performances sur le code exécuté sur 8,75 s :

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

Presque tout le temps est consacré aux appels javascript.

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

De ce temps passé en javascript, la part du lion du temps est en réalité dépensée en Sprite.calculateVertices() . 4 fois plus que ce qui est dépensé en TransformStatic.updateTransform()

Dans Firefox, j'obtiens environ le double du framerate, mais la répartition du temps passé est similaire; calculateVertices() prend la majeure partie du temps dans les deux navigateurs. Est-ce prévu ? Obtenez-vous des résultats similaires dans vos courses bunnymark ?

Ceci est attendu pour les lapins. Et les opérations matricielles dont nous parlons sont effectuées à deux endroits : upateTransform() pour la multiplication et flush() UNE FOIS PAR APPEL. Cela n'a pas d'importance à grande échelle.

Je suis confus. :( Comment puis-je connaître les performances de cette référence ? J'avais l'impression qu'elle est principalement basée sur des images par seconde pures obtenues. Si c'est vrai, alors ma fréquence d'images est _sévèrement_ limitée par les calculs de sommets. En regardant le premier diagramme que j'ai collé ci-dessus, le rendu ne représente qu'une infime fraction du temps d'image total. Les mises à jour de vertex et de transformation dépassent le temps d'image total.

Si c'est faux et que le fps n'est pas la mesure des performances de mon build pixi, quels critères dois-je utiliser pour mesurer un build donné ?

Il peut être étranglé soit sur le CPU soit sur le côté GPU (ce n'est pas montré),

Si vous avez un FPS bien inférieur à 60 mais que "inactif" est important - son GPU.

Si le ralenti est petit, alors son CPU.

calculateVertices a une opération matricielle à l'intérieur - juste une multiplication par quatre coins par matrice.

Juste une petite idée pour le fork TS : ajoutez une transformation qui inline les propriétés des types de matrice. C'est dommage qu'il n'y ait pas encore ce genre de transformations pour TS :(

Je pense que cela peut être fait pour babel aussi si nous trouvons un moyen d'annoter les variables matricielles.

Je ne peux plus comprendre ce qui se passe.

Alt text

Quelque chose à garder à l'esprit : l'objectif de ce benchmark était en premier lieu de voir si l'utilisation d'objets par rapport à gl-matrix serait un énorme impact sur les performances, comme on le pensait. Indépendamment des différences dans nos résultats de référence, nous semblons montrer de manière cohérente que les performances des objets ne sont pas pires que la plupart des cas de tableau.

Je veux également éviter de tirer des conclusions sur les performances de pixi en général ici, car j'ai profilé la marque du lapin. Dans Chrome, > 50 % du temps total du programme est consacré à Sprite.calculateVertices() (40 % ish), suivi de TransformStatic.updateTransform() (11% ish) Firefox semble fonctionner deux fois plus vite, mais le ratio du temps passé dans ces 2 fonctions est toujours cohérent.

Je veux éviter d'aller trop loin du sujet, mais je dirai qu'en faisant ce profilage bunnymark, je commence à penser que notre utilisation des getters/setters es5 pourrait avoir quelque chose à voir avec la baisse de perf dans chrome : https : //jsperf.com/es5-getters-setters-versus-getter-setter-methods/10

Est-ce que certains d'entre vous traînent ici ? Je pense qu'il serait plus facile de discuter en temps semi-réel plutôt que de passer un message ici.

Quel est ton e-mail à @mreinstein ? Vous invitera à vous relâcher 👍

D'après ce que @englercj dit, apparemment, nous ne le faisons pas. Fermeture.

Petite mise à jour : j'ai une matrice 3x3 au lieu de 3x2 dans https://github.com/pixijs/pixi-projection/blob/master/src/Matrix2d.ts , basée sur Float64Array.

Ce fil a été automatiquement verrouillé car il n'y a eu aucune activité récente après sa fermeture. Veuillez ouvrir un nouveau problème pour les bogues liés.

Cette page vous a été utile?
0 / 5 - 0 notes