Pixi.js: Proposition : Améliorer le rendu du texte

Créé le 5 avr. 2020  ·  27Commentaires  ·  Source: pixijs/pixi.js

La communauté l'a dit - les performances de rendu de texte de Pixi doivent s'améliorer. Ce numéro est consacré à la façon dont nous pouvons nous améliorer et à ce que je pense être la meilleure façon de livrer :

  • Champs de distance signés : cette technique rend le texte dans une texture (à l'aide de l'API Canvas 2D) et applique une transformation de distance. En termes simples, la transformation de distance définira la valeur de chaque pixel dans la texture de sortie à la distance de ce pixel au contour le plus proche du texte dans la texture d'entrée. pixi-sdf-text est un exemple : https://github.com/PixelsCommander/pixi-sdf-text

  • VTM : les textures vectorielles encodent les discontinuités de courbe au niveau du pixel dans les textures.

  • Rendu exact de la courbe de Bézier : cela rend la police "telle quelle" en tesselant tout sauf les courbes. Les courbes sont rendues à l'aide d'un shader de fragment spécial (aucun échantillonnage de la courbe n'est effectué, elle est rendue exactement sur le GPU avec antialiasing) : https://www.microsoft.com/en-us/research/wp-content/uploads /2005/01/p1000-loop.pdf. J'ai créé une démo pour la courbe de Bézier quadratique : https://codepen.io/sukantpal/pen/GRJawBg?editors=0010

@bigtimebuddy et moi avons discuté de ces méthodes et nous avons pensé que la troisième approche était la meilleure car :

  • Les SDF nécessitent des atlas pré-générés. La génération de SDF multicanaux est très compliquée (si nous voulons le faire au moment de l'exécution).

  • Les VTM sont beaucoup trop compliqués pour les seules polices.

  • Pour obtenir des courbes de Bézier exactes, vous devez simplement rendre la police sous forme de chemin comme tout le reste dans Graphics.

Commentaire le plus utile

@eXponenta s'il vous plaît arrêtez d'être si hostile. Nous explorons des approches rien n'est encore solide. Soyez constructif et n'utilisez pas d'insultes.

Tous les 27 commentaires

Il ne devrait pas être dans le noyau.
C'est un package lourd et DOIT ÊTRE implémenté en dehors du package principal, l'équipe principale ne devrait pas s'inquiéter de sa mise en œuvre.

Pixi SDF est un bon exemple.

Plz, n'essayez pas de faire Phaser à partir de PIXI

J'aimerais donner un peu plus de contexte. J'ai demandé à @SukantPal de rechercher des approches Text/BitmapText alternatives qui présentent différents compromis. En particulier, si nous pouvons trouver quelque chose qui fonctionne mieux que Text et qui a fière allure.

Je pense qu'il y a plusieurs approches ici. Certains peuvent être appropriés en tant que plug-ins tiers, certains peuvent être dans le référentiel mais pas regroupés, et certains peuvent remplacer/augmenter l'API actuelle. Voyons quelle est l'approche de rendu de texte la plus fructueuse qui optimise les performances mais avec moins de compromis sur BitmapText.

Il y a quelques critères que j'utiliserais pour juger un bon objet d'affichage de texte :

  • Bonnes performances d'exécution - Le rendu du texte et la modification du texte sont peu coûteux
  • Mémoire faible - L'utilisation de la RAM est faible
  • Petites dépendances - la taille du fichier d'empreinte de dépendance est petite
  • Grande fidélité - fonctionne bien avec différentes résolutions
  • Style flexible - prise en charge des traits, des ombres portées, des dégradés
  • Prend en charge les grandes langues de glyphe - par exemple, chinois, japonais, coréen
  • Rétrocompatible - Fonctionne avec Context2D

| | Perf | Mémoire | Dépendances | Fidélité | Stylisme | CJC | Context2d |
|--|--|--|--|--|--|--|--|
|Texte| | | | | | | | |
|BitmapText| | | | | | | |

D'accord.
Décrit fragment interpolation et toutes les implémentations non natives :

  1. Nécessite un shader supplémentaire et des données supplémentaires - cela empêchera le traitement par lots.
  2. Nécessite de reconstruire la géométrie lors du changement (p.3).
  3. Exiger une table de glyphes pour tous les glyphes existant dans le langage, qui nécessitent de charger totalement un TTF spécifique en mémoire, car il ne peut pas prendre en charge le streaming, puis l'analyser. On dirait que SWF.
    Parce que nous ne pouvons pas charger la police système en tant que forme, nous ne pouvons pas l'utiliser.
  4. Les règles spécifiques à la langue sont déplacées vers l'équipe lib depuis l'équipe du navigateur, qui implémentent les règles de rendu : ltr/rtl, règles de concaténation de glyphes hindi/arabe, etc. Unity ne l'a pas encore mis en œuvre correctement.
  5. Shader lourd avec des arguments supplémentaires pour prendre en charge les styles. Cela augmente le coût du rendu, car nous devrions le rendre en temps réel.
    Et bien sûr, nous devrions le mettre en cache en tant que texture.

@eXponenta

  • Le texte peut être groupé - mais pas avec d'autres éléments comme des graphiques.

  • L'implémentation actuelle du texte met en cache chaque glyphe plusieurs fois (si une lettre est utilisée plusieurs fois dans plusieurs PIXI.Texts). Il est préférable d'avoir une géométrie par glyphe. De plus, vous n'avez pas besoin de mettre en cache différentes tailles de police.

  • La géométrie n'a pas besoin d'être reconstruite à proprement parler - si les lettres du texte changent, vous devez les extraire à nouveau de la table de géométrie des glyphes. Si la transformation change, il vous suffit de changer un uniforme.

  • Nous pouvons utiliser une bibliothèque pour l'analyse des polices. La conversion en géométrie est triviale.

  • Je ne sais pas quelles sont les règles de concaténation de glyphes. Pourriez-vous expliquer?

  • De gauche à droite vs de droite à gauche - si vous voulez inverser les lettres, vous le pouvez. Si vous voulez une image miroir, définissez scale.x=-1 interne.

  • Le style du texte n'ajoutera pas plus d'arguments au shader. Cela changera la géométrie. Maintenant, stocker le glyphe en tant que géométrie prend moins de mémoire que le mettre en cache dans une texture.

  • Les fichiers de polices extraits des API doivent être mis en cache sur la machine de l'utilisateur.

Tu es très naïf :

  1. Il doit s'agir d'un batcher externe, car les glyphes nécessitent des attributs supplémentaires, tels que des ancres de Bézier, des valeurs d'ombre, des ancres de dégradé ..., ou tous doivent être transmis comme des uniformes - bye, batching.
  2. de droite à gauche et de gauche à droite, et leur mélange est un problème très complexe.
    Problèmes ouverts dans la bibliothèque de rendu PDF :
    https://github.com/asciidoctor/asciidoctor-pdf/issues/175
  3. Problème hindi (et langage similaire)
    https://docs.microsoft.com/en-us/typography/opentype/spec/gsub

Nous ne pouvons pas utiliser de dépendances externes, car ce sera le même problème que isMobile ou resource-loader .
Le noyau doit être propre. Dépendances externes minimales.

@eXponenta

  • La mise en page mixte (ltr et rtl combinés) est moins prioritaire que la haute performance. Vous pouvez toujours revenir à l'algorithme actuel. Similaire pour Devanagari. Des fonctionnalités spéciales telles que les arrêts de dégradé nécessiteront également une solution de secours.

  • Le traitement par lots peut fonctionner ici car ce sera plus facile sans aucune texture. Peut créer un plugin comme CanvasGraphicsRenderer .

D'accord. Tu gagne. Fais le. Mais en dehors du noyau, alors nous le regarderons et déciderons ce que nous devons faire ensuite.
Mais sans dépendances lourdes.

@eXponenta s'il vous plaît arrêtez d'être si hostile. Nous explorons des approches rien n'est encore solide. Soyez constructif et n'utilisez pas d'insultes.

@eXponenta Êtes-vous sûr que nous prenons en charge le texte de droite à gauche pour le moment ?

Une autre approche que nous envisageons consiste à rendre chaque glyphe à l'aide de canvas 2D (le même que maintenant) séparément et à les mettre en cache dans un atlas. L'atlas peut être réutilisé globalement pour toutes les instances PIXI.Text . Chaque combinaison glyphe + taille de police + style de police sera mise en cache séparément.

Que pensez-vous de l'acceptation de cela dans le noyau ?

Hey peeps, c'est vraiment excitant d'entendre ces nouvelles approches sur la table. @eXponenta , faisant écho à @bigtimebuddy ,

Sur le texte, voici mes 2 cents (pence?)..

Personnellement, je pense que l'approche la plus intéressante est d'obtenir la vitesse et les performances du texte bitmap, mais sans avoir besoin de créer une police bitmap au préalable. Création de textures bitmap dynamiques si vous voulez !

Polices bitmap dynamiques

avantages

  • Les outils pour créer des polices bitmap sont rares ! J'adorerais supprimer cette barrière.
  • nous pourrions créer différentes tailles de textures de taille de texte.
  • Fonctionne avec la toile
  • Des performances telles que chaque glyphe peuvent être groupées
  • beaucoup d'effets sympas !
  • L'API n'aurait besoin que d'un léger ajustement :
const textStyle = new TextStyle({
    font:'comic sans',
    fill:'bright green'
});


const bitmapFont = new BitmapFont(textStyle);

les inconvénients

  • tous les autres inconvénients des polices bitmap actuelles :P
  • la mise à jour dynamique de l'atlas des textures ralentirait-elle ?

    • Bien que cela se stabilise après un certain temps, et pourrait peut-être être mis en cache entre les sessions ?

    • Current Text doit également télécharger une texture à chaque fois qu'il change de cadre, cette approche le ferait-il moins?

idées

  • La texture du cache pourrait-elle être générée dans un stockage local ? pour les visites ultérieures ?
  • N'ajoutez que les caractères qui sont utilisés, de sorte que les textures soient aussi grandes qu'elles doivent l'être. Mettre à jour au fur et à mesure
  • cuire dans des filtres à cette texture pour des effets extra cool (contour, ombre, etc.)
  • pouvons-nous explorer la réduction de la mémoire de la police bitmap en ne faisant pas de chaque glyphe un sprite ?

FDS
Le SDF peut-il être une extension de l'approche ci-dessus ? À quel point serait-il compliqué de générer dynamiquement cette texture ? Serait-ce super janky? Parlons-nous d'augmenter considérablement la base de code ?

La plupart des gens n'ont aucune idée de ce que c'est, donc il devrait idéalement être caché. Si nous devons générer en externe les champs SDF ou que le code requis pour générer est trop volumineux ou complexe, ce serait certainement mieux à la maison en tant que plugin :

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);

Rendu exact de la courbe de Bézier

C'est une approche intéressante à coup sûr! J'avais examiné cela dans le passé et j'avais décidé que les commutateurs de shader introduits dans la boucle de rendu et la tessellation supplémentaire requise auraient une surcharge importante. @SukantPal , vous êtes plus proche de cette idée que moi en ce moment, alors j'aimerais avoir votre avis sur mes préoccupations !

Cela dit, cela vaudrait peut-être la peine de construire un prototype que nous pourrons tester et dénigrer un peu pour valider notre réflexion ?

Des moments passionnants pour le texte :D

Le SDF peut-il être une extension de l'approche ci-dessus ? À quel point serait-il compliqué de générer dynamiquement cette texture ? Serait-ce super janky? Parlons-nous d'augmenter considérablement la base de code ?

La génération de SDF est coûteuse et il y a un temps d'emballage (lorsque nous avons décidé d'emballer différentes polices dans atlas), mais elle peut être mise en cache sur le client (comme toutes les autres variantes).
Il existe une implémentation canvas (hehe):
https://github.com/mapbox/tiny-sdf
Mais la compilation SDF n'est pas tout à fait correcte (changement de glyphe lors de l'augmentation/diminution de la taille de la police)
Cela semble aussi utile, mais pour MSDF, nous devrions l'exécuter 3 fois (par canal).

oh wow, c'est un tout petit bout de code ! Cela semble mûr pour un shader à faire aussi !

@GoodBoyDigital Le

La génération SDF à canal unique ne devrait pas être trop mauvaise. Le SDF multicanal est supérieur sauf qu'il est beaucoup trop compliqué. Je ne pouvais pas comprendre comment Chumskly encodait les coins.

Maintenant, tout ce que nous envisageons ne prendra pas en charge les dispositions mixtes rtl et Ltr (comme @eXponenta l' a correctement souligné). Mais je ne pense pas que nous le soutenions officiellement pour le moment non plus.

L'approche exacte présente deux avantages - une petite taille de cache (vous mettez en cache les sommets et non tous les pixels), l'anticrénelage même lorsque le paramètre "anticrénelage" est désactivé (le texte doit toujours être anticrénelé, je pense. La mise en œuvre actuelle le fait en utilisant canvas 2D).

Quant à la démo, avez-vous regardé ma démo de courbe de Bézier quadratique ? https://codepen.io/sukantpal/pen/GRJawBg?editors=0010

——
Une autre chose dont moi et @bigtimebuddy avons discuté comme solution la plus simple était de continuer à utiliser canvas 2D et de mettre en cache les glyphes dans une texture. Les glyphes peuvent être rendus comme un quad. L'atlas des caches peut être réutilisé entre les textes. Bien sûr, cela signifie qu'un texte volumineux est mis en cache à la même taille et donc une utilisation importante de la mémoire lorsqu'une grande quantité de texte est rendue, mais qu'une partie seulement (comme la page actuelle du PDF) est visible.

Agréable,

Oui, ne vous inquiétez pas pour rtl ou ltr qui seront toujours disponibles via la méthode actuelle et c'est un problème que toutes les techniques de rendu de texte personnalisées devront résoudre à une date ultérieure.
Ces décisions. devrait idéalement se concentrer sur la consommation de mémoire et les performances d'exécution.

La démo de Bézier est cool, mais je pense que ce n'est pas suffisant pour comprendre les vraies complexités qui peuvent se cacher si nous rendons une phrase complète.

Ce que j'ai écrit sur les polices bitmap dynamiques ci-dessus est à peu près exactement ce que vous avez mentionné à propos de la mise en cache des glyphes. Je pense que c'est certainement un itinéraire utile pour descendre!

En résumé:

Les solutions de la courbe de Bézier : cela pourrait être une bonne voie, mais je pense que cela nécessite davantage de R&D pour comprendre comment cela s'intègre dans un cas d'utilisation de production réel.

Bitmapfonts dynamiques à partir de polices normales : nous devons absolument l'explorer car nous savons que cela donnera de bonnes performances tout en gardant l'API simple. Mon préféré :D

SDF monocanal J'apprécie que ce ne soit pas un aussi bon SDF multicanal, mais la démo m'a semblé plutôt bien ! Si le texte peut s'adapter correctement et que nous pouvons avoir une texture par police, alors je pense que cette voie pourrait également valoir la peine d'être étudiée !

@GoodBoyDigital Je pense que la démo a un assez bon texte. Mais il y a des différences visibles si vous essayez de chercher des détails :

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

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

Le texte sur le dessus n'est pas net - les bords ont des ondulations. Chaque combinaison police + (différences d'environ 12 à 15 pixels dans la taille de la police) devra peut-être être mise en cache séparément.

Je suis tout à fait d'accord pour opter pour SDF si ces mouvements ne sont pas importants ou si nous pouvons utiliser la mise en cache avec plusieurs tailles de police.


La solution la plus simple me convient également tbh !

Salut à tous.

Je suis un super profane sur tout ce qui concerne Pixi, mais je suis un utilisateur de GDevelop qui utilise Pixi comme backend, et j'ai creusé cela pendant un certain temps pour l'implémentation de PixiJS et le rendu de texte par GDevelop. C'est en fait impactant pour un jeu sur lequel je travaille et qui contient beaucoup de texte. (Vous pouvez voir mes recherches/exemples dans le numéro que j'ai publié dans GDevelop : https://github.com/4ian/GDevelop/issues/1449 )

L'une des options que j'ai trouvées consistait à ignorer carrément les problèmes de mise à l'échelle du texte et à les éliminer complètement en laissant toujours l'échelle du texte à 100 %, mais en mettant à l'échelle la taille de la police du texte à la place ? Ceci est agnostique pour les polices et semble fonctionner à toutes les tailles.

Voici un exemple de code où cela est fait à l'aide de Pixi : https://codepen.io/Tazy/pen/wJVExB
Je l'ai trouvé dans ce fil : https://www.html5gamedevs.com/topic/29576-scalable-text-for-pixijs/

J'ai en fait une prime sur ce problème car j'espérais que quelqu'un pourrait simplement modifier l'extension Pixi Text pour GDevelop, mais (étant le profane que je suis), je ne savais pas qu'il s'agissait d'un problème beaucoup plus important avec Pixi lui-même.

Aucune idée s'il y a des problèmes potentiels avec cette méthode, mais cela semble éviter tous les problèmes de performances de sdf/msdf/etc.

@Silver-Streak Si vous voulez dire augmenter la taille de la police par l'échelle appliquée sur l'objet d'affichage de texte, en quoi cela serait-il meilleur en termes de performances ? Les éléments SDF améliorent les performances car vous ne restituez pas avec des tailles de police plus grandes.

@Silver-Streak Bien sûr, votre proposition améliorera la _qualité_ mais je ne vois pas comment elle améliorera les performances.

@Silver-Streak Bien sûr, votre proposition améliorera la _qualité_ mais je ne vois pas comment elle améliorera les performances.

À moins que je ne me trompe, la modification de la taille de la police n'utiliserait-elle pas moins de ressources que la mise à l'échelle de l'objet entier ou le chargement dans un autre moteur de rendu ? Encore une fois, je suis super profane en ce qui concerne le fonctionnement réel de la mise à l'échelle de Pixi, il me semblait logique de laisser la police à la résolution native mais de simplement modifier la taille du texte serait plus rapide que de la mettre à l'échelle. Si c'est incorrect, désolé pour la distraction.

@Silver-Streak Scaling est implémenté en tant que transformation. Vous avez une matrice de transformation - et changer l'échelle dans cette matrice est une opération triviale.

À partir de maintenant, le texte est rendu à l'aide de l'API Canvas 2D dans un canevas. Ensuite, il est copié sur l'écran. Changer l'échelle changera simplement les coordonnées de l'écran auxquelles le texte du canevas est mappé.

Ahh, ça a du sens. C'est malheureux, bien que pour être juste, mon problème concerne davantage le rendu graphique des polices devenant flou après avoir été mis à l'échelle, mais je pensais que ce serait également de meilleures performances.

Bien que je serais ravi que quelqu'un l'implémente pour résoudre les problèmes généraux de qualité du texte, je peux tout à fait comprendre que je souhaite également améliorer les performances.

Merci d'en avoir discuté avec moi.

@Silver-Streak Pas de problème. Cependant, la mise en œuvre d'un texte axé sur la qualité ne devrait pas non plus être difficile dans WebGL. Certaines applications implémentent du texte en créant elles-mêmes un maillage. Vous pouvez analyser le fichier de police, faire une liste de sommets et le tesseler. Ces sommets peuvent être rendus à travers un maillage.

À des échelles élevées, cela peut provoquer de légers "bords" - car en fin de compte, vous rendez le texte sous forme de triangles. Pour une qualité encore plus élevée, vous pouvez utiliser le shader "courbe de Bézier exacte" dont je parlais dans ce fil - il rendra les courbes sous forme de courbes et non de triangles.

Si vous avez besoin d'aide pour la qualité du texte, je peux vous aider :)

@SukantPal Je ne suis certainement pas un contributeur à GDevelop (et vous ne voudriez pas que je le sois. Je suis principalement un analyste de systèmes d'entreprise/DevOps dans la vie, pas un développeur 😄 ) bien que j'aime ce projet et que je sois actif dans la communauté . Je sais également que d'autres membres de la communauté aimeraient voir une solution pour la qualité du texte à l'échelle.

Cependant, si vous souhaitez jeter un œil au problème dans le message, j'ai également une prime pour cela sur Bountysource également ouvert à tous. Je ne veux pas prendre plus d'espace ici, cependant, car il est évident qu'il y a une conversation beaucoup plus profonde en cours autour de Pixi en général, et ma suggestion n'était pas aussi pertinente que je le pensais.

@Silver-Streak Je pourrais jeter un œil, car je n'ai rien de mieux à faire pendant la saison corona

Je sais que c'est un fil fermé, mais je suis sûr que tout le monde cherche toujours le meilleur moyen d'améliorer le rendu du texte. J'ai remarqué que quelqu'un avait une implémentation msdf qui fonctionnait avec Pixi v5. https://github.com/cjsjy123/pixi-msdf-text-v5 J'ai pensé que je le mentionnerais aux personnes intéressées.

Ce fil devrait certainement être rouvert et maintenu en vie - ou au moins avoir une sorte de mise à jour de temps en temps, car c'est l'un des problèmes les plus recherchés.

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

Questions connexes

sntiagomoreno picture sntiagomoreno  ·  3Commentaires

st3v0 picture st3v0  ·  3Commentaires

gigamesh picture gigamesh  ·  3Commentaires

courtneyvigo picture courtneyvigo  ·  3Commentaires

samueller picture samueller  ·  3Commentaires