▼
Scroll to page 2
of
102
Optimisation des performances pour la plate-forme ADOBE® FLASH® Informations juridiques Informations juridiques Vous trouverez des informations juridiques à l’adresse http://help.adobe.com/fr_FR/legalnotices/index.html. Dernière mise à jour le 9/5/2012 iii Sommaire Chapitre 1 : Présentation Principes fondamentaux relatifs à l’exécution du code par le moteur d’exécution Perception et réalité en matière de performances Ciblage des optimisations ........................................ 1 ...................................................................... 3 ............................................................................................. 3 Chapitre 2 : Conservation de mémoire Objets d’affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Types de primitives .................................................................................................... 5 Réutilisation d’objets .................................................................................................. 7 Libération de mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Utilisation des bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Filtres et déchargement dynamique de bitmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mip-mapping direct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 Utilisation des effets 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Objets de texte et mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Modèle d’événement et rappels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Chapitre 3 : Réduction de la charge de l’unité centrale Améliorations de Flash Player 10.1 liées à l’utilisation de l’unité centrale Mode veille 20 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Figement et libération d’objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Evénements activate et deactivate Interactions de la souris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Minuteurs et événements ENTER_FRAME Interpolations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Chapitre 4 : Performances d’ActionScript 3.0 Comparaison des classes Vector et Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 API de dessin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Capture et propagation d’événement Utilisation des pixels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Expressions régulières . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Optimisations diverses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Chapitre 5 : Performances de rendu Options de retraçage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 Contenu hors-scène Qualité des clips Fusion alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Cadence de l’application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Mise en cache sous forme de bitmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Mise en cache manuelle sous forme de bitmap Rendu des objets de texte Processeur graphique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Dernière mise à jour le 9/5/2012 iv OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Sommaire Opérations asynchrones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Fenêtres transparentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Lissage des formes vectorielles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Chapitre 6 : Optimisation de l’interaction avec le réseau Amélioration en vue de l’interaction avec le réseau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Contenu externe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Erreurs d’entrée/sortie Flash Remoting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 Opérations de réseau superflues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Chapitre 7 : Utilisation des données multimédias Vidéo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 StageVideo Audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Chapitre 8 : Performances de la base de données SQL Structure d’application visant à améliorer les performances de la base de données Optimisation des fichiers de base de données Traitement superflu de la base de données à l’exécution Syntaxe SQL performante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 Performances des instructions SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 Chapitre 9 : Test de performances et déploiement Test de performances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Déploiement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Dernière mise à jour le 9/5/2012 1 Chapitre 1 : Présentation Il est possible d’exécuter les applications Adobe® AIR® et Adobe® Flash® sur de nombreuses plates-formes, notamment sur des ordinateurs de bureau, des périphériques mobiles, des tablettes et des téléviseurs. Le présent document s’appuie sur des exemples et des études de cas pour présenter les normes de bonne pratique destinées aux développeurs qui déploient ces applications. Il traite des sujets suivants : • Conservation de mémoire • Réduction de la charge de l’unité centrale • Amélioration des performances d’ActionScript 3.0 • Augmentation de la vitesse de rendu • Optimisation de l’interaction avec le réseau • Utilisation de l’audio et de la vidéo • Optimisation des performances de la base de données SQL • Test de performances et déploiement d’applications La plupart des ces optimisations s’appliquent aux applications sur tous les périphériques, qu’il s’agisse du moteur d’exécution d’AIR ou du moteur d’exécution de Flash Player. Ce document décrit également les ajouts et exceptions correspondant à certains périphériques. Certaines de ces optimisations sont centrées sur les fonctionnalités introduites dans Flash Player 10.1 et AIR 2.5. Néanmoins, la plupart d’entre elles s’appliquent également aux versions précédentes d’AIR et de Flash Player. Principes fondamentaux relatifs à l’exécution du code par le moteur d’exécution Pour comprendre comment améliorer les performances d’une application, il est essentiel de comprendre comment le moteur d’exécution de la plate-forme Flash exécute le code. Le moteur d’exécution fonctionne en boucle, certaines actions se produisant sur chaque « image ». On entend ici par image un simple bloc de temps déterminé par la cadence définie pour l’application. Le temps alloué à chaque image correspond directement à la cadence. Si vous spécifiez une cadence de 30 images par seconde, par exemple, le moteur d’exécution s’efforce de faire durer chaque image un trentième de seconde. Vous définissez la cadence initiale de l’application au moment où vous créez celle-ci. Pour ce faire, vous pouvez utiliser les paramètres correspondants d’Adobe® Flash® Builder™ ou Flash Professional. Libre à vous également de définir la cadence initiale dans le code. Pour définir la cadence d’une application basée uniquement sur ActionScript, appliquez la balise de métadonnées [SWF(frameRate="24")] à la classe du document racine. En MXML, définissez l’attribut frameRate dans la balise Application ou WindowedApplication. Chaque boucle d’image comprend deux phases, divisées en trois parties : les événements, l’événement enterFrame et le rendu. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Présentation La première phase comporte deux parties (les événements et l’événement enterFrame), qui entraînent potentiellement toutes deux l’appel de votre code. Dans la première partie de la première phase, des événements du moteur d’exécution arrivent et sont distribués. Ces événements peuvent représenter la fin ou la progression d’opérations asynchrones telles qu’une réponse à un chargement de données sur un réseau. Ils peuvent également être la conséquence d’une entrée de l’utilisateur. Au fur et à mesure de la distribution des événements, le moteur d’exécution exécute le code dans des écouteurs que vous avez enregistrés. En l’absence d’événements, le moteur d’exécution attend, sans agir, la fin de cette phase d’exécution. Il n’accélère jamais la cadence par manque d’activité. Si des événements se produisent dans d’autres parties du cycle d’exécution, le moteur d’exécution les place en file d’attente et les distribue sur l’image suivante. La deuxième partie de la première phase correspond à l’événement enterFrame. Cet événement se distingue des autres en ce qu’il est toujours distribué une fois par image. Une fois tous les événements distribués, la phase de rendu de la boucle d’image commence. A ce stade, le moteur d’exécution calcule l’état de tous les éléments visibles à l’écran et les dessine. Le processus peut alors recommencer, à l’instar d’un coureur qui fait des circuits dans un stade. Remarque : dans le cas des événements comprenant la propriété updateAfterEvent, il est possible d’imposer un rendu immédiat plutôt que d’attendre la phase de rendu. Evitez toutefois d’utiliser updateAfterEvent si elle entraîne fréquemment des problèmes de performances. Il est plus facile d’imaginer que la durée des deux phases de la boucle d’image est identique. Dans ce cas, une moitié de chaque boucle est dévolue à l’exécution des gestionnaires d’événements et du code d’application, tandis que la seconde moitié est consacrée au rendu. La réalité est néanmoins souvent toute autre. Il arrive que le code d’application utilise plus de la moitié du temps disponible dans l’image, étirant ainsi le créneau qui lui est alloué et réduisant celui du rendu. Dans d’autres cas, notamment lorsque le contenu visuel est complexe (filtres et modes de fondu, par exemple), c’est le rendu qui exige plus de la moitié du temps. La durée des phases étant variable, on dit souvent de la boucle d’image qu’elle est « élastique ». Si les opérations combinées de la boucle d’image (exécution du code et rendu) durent trop longtemps, le moteur d’exécution ne peut pas assurer une cadence uniforme. L’image s’étend au-delà du temps qui lui est alloué, retardant ainsi le déclenchement de l’image suivante. Si, par exemple, une boucle d’image dépasse un trentième de seconde, le moteur d’exécution ne peut pas mettre l’écran à jour à 30 images par seconde. Le ralentissement de la cadence se traduit par une détérioration de l’expérience de l’utilisateur. Au mieux, l’animation est saccadée ; au pire, l’application se bloque et la fenêtre est vide. Pour plus de détails sur l’exécution du code par le moteur d’exécution de la plate-forme Flash et le modèle de rendu, voir les ressources suivantes : • Flash Player Mental Model - The Elastic Racetrack (article de Ted Patrick, disponible en anglais uniquement) • Asynchronous ActionScript Execution (article de Trevor McCauley, disponible en anglais uniquement) • Optimizing Adobe AIR for code execution, memory & rendering à l’adresse http://www.adobe.com/go/learn_fp_air_perf_tv_fr (Enregistrement vidéo de la présentation de Sean Christmann lors de la conférence MAX, disponible en anglais uniquement) Dernière mise à jour le 9/5/2012 2 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Présentation Perception et réalité en matière de performances Les utilisateurs de votre application sont les ultimes juges de ses performances. Les développeurs peuvent mesurer les performances d’une application en terme de la durée d’exécution de certaines opérations ou du nombre d’occurrences d’objet créées. Ces mesures ne présentent cependant aucun intérêt pour l’utilisateur. Celui-ci mesure parfois les performances selon d’autres critères. Par exemple, l’application s’exécute-t-elle rapidement et sans saccades ? Réagitelle rapidement aux entrées ? A-t-elle un impact négatif sur les performances du système ? Pour tester les performances perçues, posez-vous les questions suivantes : • Les animations sont-elles fluides ou saccadées ? • Le contenu vidéo est-il fluide ou saccadé ? • Les clips audio s’exécutent-ils en continu ou contiennent-ils des interruptions ? • La fenêtre scintille-t-elle ou se vide-t-elle pendant les opérations de longue durée ? • Y a-t-il un décalage entre le moment où vous effectuez une saisie et l’affichage du texte ? • Si vous cliquez sur un élément, la réponse est-elle instantanée ou un délai se produit-il ? • Le ventilateur de l’unité centrale fait-il plus de bruit lorsque l’application s’exécute ? • Sur un ordinateur portable ou un périphérique mobile, la batterie se décharge-t-elle rapidement lors de l’exécution de l’application ? • Les autres applications réagissent-elles plus lentement lorsque l’application s’exécute ? Il est important de faire la distinction entre perception et réalité, car vous ne procéderez pas nécessairement de même pour optimiser les performances perçues ou pour accélérer au maximum les performances. Veillez à ce que l’application n’exécute jamais de tels volumes de code que le moteur d’exécution se trouve dans l’impossibilité de mettre à jour l’écran et de recueillir les entrées de l’utilisateur à fréquence régulière. Pour parvenir à cet équilibre, il est parfois nécessaire de diviser une tâche de programme en plusieurs parties, entre lesquelles le moteur d’exécution pourra mettre l’écran à jour (voir « Performances de rendu » à la page 49 pour plus de détails). Les astuces et techniques décrites ci-après visent à vous permettre d’optimiser l’exécution du code lui-même et les performances perçues par l’utilisateur. Ciblage des optimisations Certaines améliorations des performances ne font pas de différence notoire pour l’utilisateur. Il est important de bien cibler les optimisations sur les zones problématiques de l’application concernée. Certaines techniques d’optimisation sont bonnes à mettre en pratique dans tous les cas. Pour d’autres, ce sont les exigences de l’application et la base d’utilisateurs visée qui en déterminent l’utilité. Il est vrai, par exemple, que les applications sont plus performantes si vous éliminez toute animation ou vidéo, de même que les filtres graphiques et les effets. Ce sont cependant ses fonctionnalités multimédias et graphiques qui sont l’une des raisons d’utiliser la plate-forme Flash pour créer des applications riches et expressives. Déterminez si le niveau de complexité souhaité est adapté aux performances caractéristiques des machines et périphériques sur lesquelles l’application s’exécutera. Suivez ce conseil courant : « Ne cherchez pas à optimiser les performances trop tôt. » Certaines optimisations nécessitent de programmer du code peu lisible ou plus rigide. Il est alors plus difficile d’assurer la maintenance de ce code une fois qu’il est optimisé. Dans ce cas, il est souvent préférable d’attendre pour déterminer si les performances d’une portion spécifique du code sont médiocres avant de décider de son optimisation. Dernière mise à jour le 9/5/2012 3 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Présentation L’amélioration des performances exige souvent des compromis. En théorie, la réduction de la quantité de mémoire consommée par une application se traduit par une accélération de la vitesse d’exécution d’une tâche par l’application. En pratique, ce type d’amélioration n’est pas toujours possible. Imaginons que l’application se bloque pendant une opération. Pour résoudre ce problème, il est souvent nécessaire de répartir des tâches sur plusieurs images. Cette division se soldera, selon toute probabilité, par un ralentissement global du processus. Il se peut toutefois que l’utilisateur ne remarque pas le temps supplémentaire, car l’application continue de répondre à ses entrées et ne se bloque pas. Pour identifier les éléments à optimiser et déterminer l’utilité des optimisations, il est essentiel d’effectuer des tests de performances. Vous trouverez des techniques et des conseils à ce sujet à la section « Test de performances et déploiement » à la page 97. Pour plus d’informations sur la façon de déterminer les parties d’une application qu’il serait judicieux d’optimiser, voir les ressources suivantes : • Performance-tuning apps for AIR à l’adresse http://www.adobe.com/go/learn_fp_goldman_tv_fr (Enregistrement vidéo de la présentation d’Oliver Goldman lors de la conférence MAX. Disponible en anglais uniquement) • Performance-tuning Adobe AIR applications à l’adresse http://www.adobe.com/go/learn_fp_air_perf_devnet_fr (Article d’Oliver Goldman, fondé sur la présentation, sur Adobe Developer Connection. Disponible en anglais uniquement.) Dernière mise à jour le 9/5/2012 4 5 Chapitre 2 : Conservation de mémoire Lors du développement d’applications, y compris les applications de bureau, il est toujours important de conserver la mémoire. Les périphériques mobiles sont toutefois particulièrement gourmands en mémoire et il est souhaitable de limiter la quantité de mémoire que consomme une application. Objets d’affichage Sélectionnez un objet d’affichage approprié. ActionScript 3.0 propose un grand nombre d’objets d’affichage. Une des plus simples techniques d’optimisation visant à limiter la consommation de mémoire consiste à choisir le type approprié d’objet d’affichage. Pour créer des formes simples qui ne sont pas interactives, utilisez les objets Shape. Pour créer des objets interactifs ne nécessitant pas de scénario, faites appel aux objets Spirite. Pour une animation s’appuyant sur un scénario, recourez aux objets MovieClip. Choisissez toujours le type d’objet le plus performant pour l’application. Le code suivant indique la quantité de mémoire utilisée par différents objets d’affichage : trace(getSize(new Shape())); // output: 236 trace(getSize(new Sprite())); // output: 412 trace(getSize(new MovieClip())); // output: 440 La méthode getSize() indique la quantité de mémoire, exprimée en nombre d’octets, que consomme un objet. Vous pouvez constater que l’utilisation de plusieurs objets MovieClip, plutôt que des objets Shape simples, peut gaspiller de la mémoire si les fonctionnalités d’un objet MovieClip ne sont pas nécessaires. Types de primitives La méthode getSize() permet de tester les performances du code et de déterminer l’objet le plus adapté à la tâche concernée. Tous les types de primitives, à l’exception de String, requièrent 4 à 8 octets de mémoire. Aucun type spécifique de primitive ne permet d’optimiser l’utilisation de la mémoire : Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire // Primitive types var a:Number; trace(getSize(a)); // output: 8 var b:int; trace(getSize(b)); // output: 4 var c:uint; trace(getSize(c)); // output: 4 var d:Boolean; trace(getSize(d)); // output: 4 var e:String; trace(getSize(e)); // output: 4 Si vous n’attribuez pas de valeur à une primitive Number, qui représente une valeur 64 bits, la machine virtuelle ActionScript (AVM) lui alloue 8 octets. Tous les autres types de primitives sont stockés dans 4 octets. // Primitive types var a:Number = 8; trace(getSize(a)); // output: 4 a = Number.MAX_VALUE; trace(getSize(a)); // output: 8 Le comportement du type String est différent. La quantité d’espace de stockage alloué est fonction de la longueur de la chaîne : var name:String; trace(getSize(name)); // output: 4 name = ""; trace(getSize(name)); // output: 24 name = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."; trace(getSize(name)); // output: 1172 La méthode getSize() permet de tester les performances du code et de déterminer l’objet le plus adapté à la tâche concernée. Dernière mise à jour le 9/5/2012 6 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Réutilisation d’objets Réutilisez des objets, si possible, au lieu de les recréer. Une autre technique simple d’optimisation de la mémoire consiste à réutiliser les objets pour éviter, dans la mesure possible, de les recréer. N’utilisez pas le code suivant dans une boucle, par exemple : const MAX_NUM:int = 18; const COLOR:uint = 0xCCCCCC; var area:Rectangle; for (var:int = 0; i < MAX_NUM; i++) { // Do not use the following code area = new Rectangle(i,0,1,10); myBitmapData.fillRect(area,COLOR); } Le fait de recréer l’objet Rectangle dans chaque itération de la boucle utilise plus de mémoire et est une procédure plus lente, car un objet est créé à chaque itération. Procédez plutôt comme suit : const MAX_NUM:int = 18; const COLOR:uint = 0xCCCCCC; // Create the rectangle outside the loop var area:Rectangle = new Rectangle(0,0,1,10); for (var:int = 0; i < MAX_NUM; i++) { area.x = i; myBitmapData.fillRect(area,COLOR); } L’exemple précédent reposait sur un objet dont l’impact sur la mémoire était relativement faible. L’exemple suivant montre comment conserver encore plus de mémoire en réutilisant un objet BitmapData. Le code ci-dessous, qui crée un effet de mosaïque, gaspille de la mémoire : Dernière mise à jour le 9/5/2012 7 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire var myImage:BitmapData; var myContainer:Bitmap; const MAX_NUM:int = 300; for (var i:int = 0; i< MAX_NUM; i++) { // Create a 20 x 20 pixel bitmap, non-transparent myImage = new BitmapData(20,20,false,0xF0D062); // Create a container for each BitmapData instance myContainer = new Bitmap(myImage); // Add it to the display list addChild(myContainer); // Place each container myContainer.x = (myContainer.width + 8) * Math.round(i % 20); myContainer.y = (myContainer.height + 8) * int(i / 20); } Remarque : lorsque vous utilisez des valeurs positives, il est beaucoup plus rapide d’associer la valeur arrondie à int que d’utiliser la méthode Math.floor(). L’image suivante illustre le résultat de l’effet de mosaïque sur le bitmap : Effet de mosaïque résultant Une version optimisée crée une occurrence unique de BitmapData référencée par plusieurs occurrences de Bitmap et donne le même résultat : Dernière mise à jour le 9/5/2012 8 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire // Create a single 20 x 20 pixel bitmap, non-transparent var myImage:BitmapData = new BitmapData(20,20,false,0xF0D062); var myContainer:Bitmap; const MAX_NUM:int = 300; for (var i:int = 0; i< MAX_NUM; i++) { // Create a container referencing the BitmapData instance myContainer = new Bitmap(myImage); // Add it to the display list addChild(myContainer); // Place each container myContainer.x = (myContainer.width + 8) * Math.round(i % 20); myContainer.y = (myContainer.height + 8) * int(i / 20); } Cette technique conserve environ 700 Ko de mémoire, ce qui est considérable sur un périphérique mobile standard. Les propriétés de Bitmap permettent de manipuler tout conteneur de bitmap sans incidence sur l’occurrence de BitmapData originale : // Create a single 20 x 20 pixel bitmap, non-transparent var myImage:BitmapData = new BitmapData(20,20,false,0xF0D062); var myContainer:Bitmap; const MAX_NUM:int = 300; for (var i:int = 0; i< MAX_NUM; i++) { // Create a container referencing the BitmapData instance myContainer = new Bitmap(myImage); // Add it to the DisplayList addChild(myContainer); // Place each container myContainer.x = (myContainer.width + 8) * Math.round(i % 20); myContainer.y = (myContainer.height + 8) * int(i / 20); // Set a specific rotation, alpha, and depth myContainer.rotation = Math.random()*360; myContainer.alpha = Math.random(); myContainer.scaleX = myContainer.scaleY = Math.random(); } L’image suivante illustre le résultat de l’effet des transformations du bitmap : Dernière mise à jour le 9/5/2012 9 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Résultat des transformations du bitmap Voir aussi « Mise en cache sous forme de bitmap » à la page 55 Pool d’objets Faites appel à la technique de pool d’objets, dans la mesure du possible. Le pool d’objets, qui consiste à réutiliser des objets à terme, est une autre technique d’optimisation importante. Vous créez un nombre défini d’objets lors de l’initialisation de l’application et les enregistrez dans un pool, tel qu’un objet Array ou Vector. Lorsque vous en avez terminé avec un objet, vous le désactivez pour éviter qu’il ne consomme des ressources de l’unité centrale et vous supprimez toutes les références mutuelles. Vous ne définissez toutefois pas les références sur null, ce qui rendrait l’objet éligible pour le processus de nettoyage de la mémoire. Vous vous contentez de lui faire réintégrer le pool et de l’en extraire lorsque vous avez besoin d’un nouvel objet. Lorsque vous réutilisez des objets, il n’est pas autant nécessaire de les instancier, processus gourmand en ressources. Elle limite également le déclenchement du nettoyeur de mémoire, qui est susceptible de ralentir l’application. Le code suivant illustre la technique de pool d’objets : Dernière mise à jour le 9/5/2012 10 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire package { import flash.display.Sprite; public final class { private static private static private static private static private static SpritePool var var var var var MAX_VALUE:uint; GROWTH_VALUE:uint; counter:uint; pool:Vector.<Sprite>; currentSprite:Sprite; public static function initialize( maxPoolSize:uint, growthValue:uint ):void { MAX_VALUE = maxPoolSize; GROWTH_VALUE = growthValue; counter = maxPoolSize; var i:uint = maxPoolSize; pool = new Vector.<Sprite>(MAX_VALUE); while( --i > -1 ) pool[i] = new Sprite(); } public static function getSprite():Sprite { if ( counter > 0 ) return currentSprite = pool[--counter]; var i:uint = GROWTH_VALUE; while( --i > -1 ) pool.unshift ( new Sprite() ); counter = GROWTH_VALUE; return getSprite(); } public static function disposeSprite(disposedSprite:Sprite):void { pool[counter++] = disposedSprite; } } } La classe SpritePool crée un pool de nouveaux objets lors de l’initialisation de l’application. La méthode getSprite() renvoie des occurrences de ces objets tandis que la méthode disposeSprite() les libère. Le code autorise l’expansion du pool une fois celui-ci entièrement consommé. Il est également possible de créer un pool de taille fixe, qui une fois épuisé, interdit l’allocation de nouveaux objets. Evitez si possible de créer des objets dans des boucles. Pour plus d’informations, voir « Libération de mémoire » à la page 12. Dans le code suivant, la classe SpritePool extrait de nouvelles occurrences : Dernière mise à jour le 9/5/2012 11 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire const MAX_SPRITES:uint = 100; const GROWTH_VALUE:uint = MAX_SPRITES >> 1; const MAX_NUM:uint = 10; SpritePool.initialize ( MAX_SPRITES, GROWTH_VALUE ); var currentSprite:Sprite; var container:Sprite = SpritePool.getSprite(); addChild ( container ); for ( var i:int = 0; i< MAX_NUM; i++ ) { for ( var j:int = 0; j< MAX_NUM; j++ ) { currentSprite = SpritePool.getSprite(); currentSprite.graphics.beginFill ( 0x990000 ); currentSprite.graphics.drawCircle ( 10, 10, 10 ); currentSprite.x = j * (currentSprite.width + 5); currentSprite.y = i * (currentSprite.width + 5); container.addChild ( currentSprite ); } } Le code suivant supprime tous les objets de la liste d’affichage lorsque l’utilisateur clique sur la souris et les réutilise ultérieurement à d’autres fins : stage.addEventListener ( MouseEvent.CLICK, removeDots ); function removeDots ( e:MouseEvent ):void { while (container.numChildren > 0 ) SpritePool.disposeSprite (container.removeChildAt(0) as Sprite ); } Remarque : le vecteur de pool fait toujours référence aux objets Sprite. Si vous souhaitez définitivement supprimer l’objet de la mémoire, appliquez la méthode dispose() à la classe SpritePool : elle efface toutes les références restantes. Libération de mémoire Supprimez toutes les références aux objets pour activer le déclenchement du nettoyage de la mémoire. Il est impossible de démarrer directement le nettoyeur de mémoire dans la version commerciale de Flash Player. Pour être certain qu’un objet est collecté par le nettoyeur, supprimez toutes ses références. Rappelez-vous que l’opérateur delete d’ActionScript 1.0 et 2.0 se comporte différemment dans ActionScript 3.0. Il permet uniquement de supprimer des propriétés dynamiques sur un objet dynamique. Remarque : vous pouvez appeler directement le nettoyeur de mémoire dans Adobe® AIR® et dans la version de débogage de Flash Player. Le code suivant, par exemple, définit une référence Sprite sur null : Dernière mise à jour le 9/5/2012 12 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire var mySprite:Sprite = new Sprite(); // Set the reference to null, so that the garbage collector removes // it from memory mySprite = null; Un objet défini sur null n’est pas nécessairement supprimé de la mémoire. Il arrive que le nettoyeur de mémoire ne s’exécute pas si la quantité de mémoire disponible est considérée comme suffisante. Le nettoyage de la mémoire est un processus imprévisible. L’affectation de mémoire, et non la suppression d’objets, déclenche le nettoyage de la mémoire. Lorsqu’il s’exécute, il détecte des graphes d’objets qui n’ont pas encore été nettoyés. Il détecte les objets inactifs dans ces graphes en identifiant les objets qui se font référence mais que l’application n’utilise plus, et les supprime. Dans une application de grande taille, ce processus, qui est susceptible de solliciter fortement l’unité centrale, peut affecter les performances et entraîner un ralentissement notable de l’application. Réutilisez autant que possible les objets pour essayer de réduire le nombre d’exécutions du nettoyeur de mémoire. Définissez également les références sur null, le cas échéant, afin que le nettoyeur consacre moins de temps de traitement à rechercher les objets. Vous pourriez envisager le nettoyage de la mémoire comme une assurance : gérez donc la durée de vie des objets de manière explicite et systématique, dans la mesure du possible. Remarque : définir une référence à un objet d’affichage sur null ne garantit pas le figement de l’objet. L’objet continue de consommer les ressources de l’unité centrale jusqu’à ce qu’il soit nettoyé. Veillez à désactiver votre objet avant de définir sa référence sur null. Vous pouvez lancer le nettoyeur de mémoire à l’aide de la méthode System.gc(), que proposent Adobe AIR et la version de débogage de Flash Player. Le profileur livré avec Adobe® Flash® Builder™ permet de lancer manuellement le nettoyeur de mémoire. L’exécution de ce dernier permet de vérifier le comportement de l’application et de déterminer si les objets sont correctement supprimés de la mémoire. Remarque : tout objet servant d’écouteur d’événements peut être référencé par un autre objet. Dans ce cas, supprimez les écouteurs d’événements à l’aide de la méthode removeEventListener() avant de définir les références sur null. Il est heureusement possible de réduire instantanément la quantité de mémoire utilisée par les bitmaps. La classe BitmapData, par exemple, possède une méthode dispose(). L’exemple qui suit crée une occurrence de BitmapData de 1,8 Mo. La mémoire utilisée actuellement atteint 1,8 Mo et la propriété System.totalMemory renvoie une valeur inférieure : trace(System.totalMemory / 1024); // output: 43100 // Create a BitmapData instance var image:BitmapData = new BitmapData(800, 600); trace(System.totalMemory / 1024); // output: 44964 L’occurrence de BitmapData est ensuite manuellement supprimée de la mémoire, qui est à nouveau vérifiée : Dernière mise à jour le 9/5/2012 13 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire trace(System.totalMemory / 1024); // output: 43100 // Create a BitmapData instance var image:BitmapData = new BitmapData(800, 600); trace(System.totalMemory / 1024); // output: 44964 image.dispose(); image = null; trace(System.totalMemory / 1024); // output: 43084 Bien que la méthode dispose() supprime les pixels de la mémoire, il est néanmoins nécessaire de définir la référence sur null pour la libérer totalement. Appelez systématiquement la méthode dispose() et définissez la référence sur null lorsque vous n’avez plus besoin d’un objet BitmapData afin de libérer immédiatement la mémoire. Remarque : la classe System de Flash Player 10.1 et d’AIR 1.5.2 comporte une nouvelle méthode, disposeXML(). Cette méthode vous permet de mettre immédiatement un objet XML à la disposition du nettoyeur de mémoire, en transmettant l’arborescence XML en tant que paramètre. Voir aussi « Figement et libération d’objets » à la page 28 Utilisation des bitmaps L’utilisation de vecteurs et non de bitmaps est un bon moyen d’économiser de la mémoire. Cependant, les vecteurs sollicitent beaucoup l’unité centrale et le processeur graphique, particulièrement s’ils sont nombreux. Les bitmaps, quant à eux, permettent d’optimiser le rendu, car le moteur d’exécution nécessite moins de ressources de traitement pour dessiner des pixels à l’écran que pour effectuer le rendu du contenu des vecteurs. Voir aussi « Mise en cache manuelle sous forme de bitmap » à la page 63 Sous-échantillonnage des bitmaps Pour assurer une meilleure utilisation de la mémoire, les images opaques de 32 bits sont réduites en images de 16 bits lorsque Flash Player détecte un écran 16 bits. Ce sous-échantillonnage nécessite la moitié des ressources de mémoire et le rendu des images est plus rapide. Cette fonction est uniquement disponible dans Flash Player 10.1 pour Windows Mobile. Remarque : avant Flash Player 10.1, tous les pixels créés en mémoire étaient stockés dans 32 bits (4 octets). Un simple logo de 300 x 300 pixels exigeait 350 Ko de mémoire (300*300*4/1024). Grâce à ce nouveau comportement, le même logo opaque requiert uniquement 175 Ko. Si le logo est transparent, il n’est pas sous-échantillonné à 16 bits et sa taille en mémoire ne change pas. Cette fonction s’applique uniquement aux bitmaps intégrés ou aux images chargées à l’exécution (PNG, GIF, JPG). Dernière mise à jour le 9/5/2012 14 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Sur les périphériques mobiles, il est parfois difficile de faire la différence entre le rendu d’une image en 16 bits ou en 32 bits. Sur une image simple ne comportant que quelques couleurs, aucune différence n’est détectable. Même sur une image plus complexe, les différences sont peu notables. Une certaine dégradation des couleurs peut cependant se produire lorsque l’utilisateur effectue un zoom avant sur l’image, et un dégradé de 16 bits est moins régulier que la version 32 bits. Référence unique à BitmapData Il est important d’optimiser l’utilisation de la classe BitmapData en réutilisant les occurrences autant que faire se peut. Flash Player 10.1 et AIR 2.5 proposent une nouvelle fonction de référence unique à BitmapData sur toutes les platesformes. Lors de la création d’occurrences de BitmapData à partir d’une image intégrée, une version unique du bitmap est utilisée pour toutes ces occurrences. Si le bitmap est modifié par la suite, un bitmap qui lui est propre lui est attribué en mémoire. L’image intégrée peut provenir de la bibliothèque ou d’une balise [Embed]. Remarque : cette nouvelle fonction présente également des avantages pour le contenu existant, car Flash Player 10.1 et AIR 2.5 réutilisent automatiquement les bitmaps. Lors de l’instanciation d’une image intégrée, un bitmap associé est créé en mémoire. Préalablement à Flash Player 10.1 et AIR 2.5, à chaque occurrence était attribué son propre bitmap en mémoire, comme illustré ci-dessous : Mémoire Affiché Bitmap source Occurrence du logo Bitmap source Occurrence du logo Bitmaps en mémoire avant Flash Player 10.1 et AIR 2.5 Dans Flash Player 10.1 et AIR 2.5, lors de la création de plusieurs occurrences d’une même image, une version unique du bitmap est utilisée pour toutes les occurrences de BitmapData. Ce concept est illustré ci-dessous : Dernière mise à jour le 9/5/2012 15 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Mémoire Affiché Occurrence du logo Bitmap source Occurrence du logo Bitmaps en mémoire dans Flash Player 10.1 et AIR 2.5 Cette technique réduit considérablement la quantité de mémoire que nécessite une application utilisant de nombreux bitmaps. Le code suivant crée plusieurs occurrences du symbole Star : const MAX_NUM:int = 18; var star:BitmapData; var bitmap:Bitmap; for (var i:int = 0; i<MAX_NUM; i++) { for (var j:int = 0; j<MAX_NUM; j++) { star = new Star(0,0); bitmap = new Bitmap(star); bitmap.x = j * star.width; bitmap.y = i * star.height; addChild(bitmap) } } La figure suivante illustre le résultat du code : Dernière mise à jour le 9/5/2012 16 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Résultat du code utilisé pour créer plusieurs occurrences du symbole Avec Flash Player 10, par exemple, l’animation ci-dessus utilise environ 1 008 Ko de mémoire. Avec Flash Player 10.1, l’animation utilise seulement 4 Ko, que l’application soit installée sur un ordinateur de bureau ou sur un périphérique mobile. Le code suivant modifie une seule occurrence de BitmapData : const MAX_NUM:int = 18; var star:BitmapData; var bitmap:Bitmap; for (var i:int = 0; i<MAX_NUM; i++) { for (var j:int = 0; j<MAX_NUM; j++) { star = new Star(0,0); bitmap = new Bitmap(star); bitmap.x = j * star.width; bitmap.y = i * star.height; addChild(bitmap) } } var ref:Bitmap = getChildAt(0) as Bitmap; ref.bitmapData.pixelDissolve(ref.bitmapData, ref.bitmapData.rect, new Point(0,0),Math.random()*200,Math.random()*200, 0x990000); La figure suivante illustre le résultat de la modification d’une seule occurrence de Star : Dernière mise à jour le 9/5/2012 17 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Résultat de la modification d’une occurrence unique En interne, le moteur d’exécution attribue et crée automatiquement un bitmap en mémoire pour gérer les modifications au niveau des pixels. L’appel d’une méthode de la classe BitmapData entraîne la modification des pixels et la création d’une occurrence en mémoire, et aucune autre occurrence n’est mise à jour. La figure suivante illustre ce concept : Mémoire Affiché Occurrence du logo Bitmap source Occurrence du logo setPixel() Bitmap source Occurrence du logo Résultat en mémoire de la modification d’un bitmap unique Si une étoile est modifiée, une nouvelle copie est créée dans la mémoire. L’animation résultante utilise environ 8 Ko de mémoire dans Flash Player 10.1 et AIR 2.5. Dernière mise à jour le 9/5/2012 18 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Dans l’exemple précédent, chaque bitmap peut être transformé individuellement. Pour créer uniquement l’effet de mosaïque, la méthode beginBitmapFill() est la plus adaptée : var container:Sprite = new Sprite(); var source:BitmapData = new Star(0,0); // Fill the surface with the source BitmapData container.graphics.beginBitmapFill(source); container.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight); addChild(container); Cette technique donne le même résultat et une seule occurrence de BitmapData est créée. Pour imprimer une rotation permanente aux étoiles, plutôt que d’accéder à chaque occurrence de Star, utilisez un objet Matrix pivotant sur chaque image et transmettez-le à la méthode beginBitmapFill() : var container:Sprite = new Sprite(); container.addEventListener(Event.ENTER_FRAME, rotate); var source:BitmapData = new Star(0,0); var matrix:Matrix = new Matrix(); addChild(container); var angle:Number = .01; function rotate(e:Event):void { // Rotate the stars matrix.rotate(angle); // Clear the content container.graphics.clear(); // Fill the surface with the source BitmapData container.graphics.beginBitmapFill(source,matrix,true,true); container.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight); } Il est ainsi inutile de recourir à une boucle ActionScript pour créer l’effet. Le moteur d’exécution effectue toutes les opérations en interne. La figure suivante illustre le résultat de la transformation des étoiles : Dernière mise à jour le 9/5/2012 19 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Résultat de la rotation des étoiles La mise à jour de l’objet BitmapData source original est automatiquement répercutée sur son utilisation à un autre emplacement sur la scène. Cette technique peut se révéler très performante. Elle ne permet pas cependant de mettre individuellement à l’échelle chaque étoile, comme dans l’exemple précédent. Remarque : lors de l’utilisation de plusieurs occurrences d’une même image, le dessin varie selon qu’une classe est associée au bitmap original en mémoire. Si aucune classe n’est associée au bitmap, les images sont dessinées en tant qu’objets Shape avec des remplissages de bitmap. Filtres et déchargement dynamique de bitmaps Evitez les filtres, y compris ceux traités par le biais de Pixel Bender. Utilisez un minimum d’effets tels que les filtres, y compris ceux traités sur les périphériques mobiles par le biais de Pixel Bender. Lors de l’application d’un filtre à un objet d’affichage, le moteur d’exécution crée deux bitmaps en mémoire, chacun de la taille de l’objet d’affichage. Le premier est une version pixellisée de l’objet d’affichage et sert à créer le second, auquel le filtre est appliqué : Dernière mise à jour le 9/5/2012 20 21 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Mémoire Affiché Version du bitmap non filtrée Résultat Version du bitmap filtrée Deux bitmaps en mémoire lors de l’application d’un filtre Lors de la modification de l’une des propriétés d’un filtre, les deux bitmaps sont mis à jour en mémoire pour créer le bitmap résultant. Ce processus sollicite l’unité centrale et les deux bitmaps peuvent nécessiter beaucoup de mémoire. Flash Player 10.1 et AIR 2.5 proposent un nouveau comportement de filtrage sur toutes les plates-formes. Si le filtre n’est pas modifié sous 30 secondes, ou s’il est masqué ou hors écran, la mémoire utilisée par le bitmap non filtré est libérée. Cette fonction économise donc la moitié de la mémoire exigée par un filtre sur toutes les plates-formes. Considérez par exemple un objet de texte auquel est appliqué un filtre de flou. Le texte en question est purement décoratif et ne subit aucune modification. Après 30 secondes, le bitmap non filtré est libéré de la mémoire. Il se produit le même résultat si le texte est masqué pendant 30 secondes ou est hors écran. Lors de la modification de l’une des propriétés du filtre, le bitmap non filtré en mémoire est recréé. Cette fonction s’appelle déchargement dynamique de bitmap. Même avec ces optimisations, utilisez les filtres avec précaution. Leur modification sollicite énormément l’unité centrale ou le processeur graphique. Il est recommandé de créer des bitmaps dans un outil de création, tel qu’Adobe® Photoshop®, pour émuler les filtres, si possible. Evitez l’utilisation de bitmaps dynamiques créés à l’exécution dans ActionScript. L’utilisation de bitmaps créés en externe permet au moteur d’exécution de réduire la charge de l’unité centrale ou du processeur graphique, surtout si les propriétés du filtre ne changent pas à terme. Si possible, créez les effets requis sur un bitmap dans un outil de création. Vous pouvez ensuite afficher ce bitmap dans le moteur d’exécution sans le traiter, ce qui est beaucoup plus rapide. Mip-mapping direct Utilisez le mip-mapping pour mettre à l’échelle les images volumineuses, si besoin est. Flash Player 10.1 et AIR 2.5 proposent, sur toutes les plates-formes, une autre nouvelle fonction liée au mip-mapping. Flash Player 9 et AIR 1.0 offraient une fonction de mip-mapping qui permettait d’améliorer la qualité et les performances des bitmaps sous-échantillonnés. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Remarque : le mip-mapping s’applique uniquement aux images chargées dynamiquement ou aux bitmaps intégrés ; il ne s’applique pas aux objets d’affichage filtrés ou mis en cache. Il est uniquement traité si la largeur et la hauteur du bitmap sont des nombres pairs. Si la hauteur ou la largeur est un nombre impair, le mip-mapping s’arrête. Ainsi, il est possible d’appliquer un mip-mapping à une image de 250 x 250 pour la réduire à 125 x 125 au maximum. Dans ce cas, en effet, l’une des dimensions au moins est un nombre impair. Les bitmaps dont les dimensions sont des puissances de deux permettent d’obtenir des résultats optimaux. Exemple : 256 x 256, 512 x 512, 1024 x 1024, etc. Imaginons qu’une image de 1024 x 1024 est chargée et qu’un développeur souhaite la mettre à l’échelle pour créer une vignette dans une galerie. La fonction de mip-mapping effectue correctement le rendu de l’image lors de la mise à l’échelle en utilisant les versions sous-échantillonnées intermédiaires du bitmap en tant que textures. Les versions antérieures du moteur d’exécution créaient des versions sous-échantillonnées intermédiaires du bitmap en mémoire. Si une image de 1024 x 1024 était chargée et affichée à 64 x 64, les anciennes versions du moteur d’exécution créaient un bitmap pour chaque demi-taille ; dans notre exemple, 512 x 512, 256 x 256, 128 x 128 et 64 x 64. Flash Player 10.1 et AIR 2.5 permettent à présent d’effectuer un mip-mapping direct de la taille originale à la taille de destination. Dans l’exemple précédent, seuls seraient créés le bitmap original de 4 Mo (1024 x 1024) et le bitmap de 16 Ko (64 x 64) auquel un mip-mapping a été appliqué. La logique de mip-mapping est également compatible avec le déchargement dynamique de bitmap. Si seul le bitmap de 64 x 64 est utilisé, l’original de 4 Mo est vidé de la mémoire. S’il est nécessaire de répéter le mip-mapping, cet original est rechargé. Par ailleurs, si d’autres bitmaps de plusieurs tailles ayant fait l’objet d’un mip-mapping sont nécessaires, la chaîne de mip-mapping intermédiaire est utilisée pour les créer. S’il est nécessaire de créer un bitmap au 1:8, par exemple, les bitmaps au 1:4, 1:2 et 1:1 sont examinés pour déterminer lequel d’entre eux est chargé en mémoire en premier. En l’absence des autres versions, le bitmap original, au 1:1, est chargé à partir de la ressource et utilisé. Le décompresseur JPEG peut effectuer un mip-mapping dans son propre format. Ce mip-mapping direct permet de décompresser directement un bitmap de grande taille vers un format mip-map sans charger intégralement l’image non compressée. La génération du mip-map est considérablement plus rapide. La mémoire exigée par les bitmaps volumineux n’est pas allouée et, donc, libérée. La qualité d’image JPEG est comparable à la technique de mip-mapping générale. Remarque : évitez une utilisation excessive du mip-mapping. Bien qu’il améliore la qualité des images téléchargées, il n’est pas sans incidence sur la bande passante, la mémoire et la vitesse. Dans certains cas, il est préférable de mettre à l’échelle une version du bitmap dans un outil externe et de l’importer dans votre application. Ne créez pas des bitmaps de grande taille si vous allez les réduire plus tard. Utilisation des effets 3D Envisagez de créer manuellement les effets 3D. Flash Player 10 et AIR 1.5 ont introduit un nouveau moteur 3D, qui permet d’appliquer des transformations de perspective aux objets d’affichage. Vous pouvez appliquer ces transformations à l’aide des propriétés rotationX et rotationY ou de la méthode drawTriangles() de la classe Graphics. Vous pouvez aussi définir un effet de profondeur à l’aide de la propriété z. Gardez à l’esprit que chaque objet d’affichage auquel une transformation de perspective est appliquée est pixellisé en tant que bitmap et exige donc plus de mémoire. La figure suivante illustre l’anticrènelage résultant de la pixellisation lors de l’application d’une transformation de perspective : Dernière mise à jour le 9/5/2012 22 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Anticrènelage résultant de l’application d’une transformation de perspective L’anticrènelage est produit par la pixellisation dynamique sous forme de bitmap du contenu du vecteur. L’anticrènelage est appliqué lors de l’utilisation d’effets 3D dans la version de bureau d’AIR et de Flash Player, et dans la version mobile d’AIR 2.0.1 et d’AIR 2.5. L’anticrènelage n’est toutefois pas appliqué dans la version mobile de Flash Player. Vous pouvez économiser de la mémoire s’il vous est possible de créer l’effet 3D manuellement sans faire appel à l’API native. Toutefois, les nouvelles fonctions 3D introduites dans Flash Player 10 et AIR 1.5 facilitent le mappage des textures, car les méthodes comme drawTriangles() gèrent ce processus en natif. Il vous appartient, en tant que développeur, de décider si les performances de l’effet 3D recherché sont optimisées par un traitement manuel ou via l’API native. Outre les considérations liées à la mémoire, tenez également compte des performances d’exécution et de rendu d’ActionScript. Dans les applications mobiles d’AIR 2.0.1 et d’AIR 2.5 dans lesquelles vous définissez la propriété renderMode sur GPU, c’est le processeur graphique qui se charge des transformations 3D. En revanche, si la propriété renderMode est définie sur CPU, c’est l’unité centrale (et non le processeur graphique) qui effectue les transformations 3D. Dans les applications de Flash Player 10.1, c’est l’unité centrale qui effectue les transformations 3D. Lorsque l’unité centrale effectue les transformations 3D, tenez compte du fait que l’application d’une transformation 3D à un objet d’affichage nécessite deux bitmaps en mémoire. Un bitmap est nécessaire pour le bitmap source et un autre pour la version à laquelle une transformation de perspective est appliquée. Le comportement des transformations 3D est similaire à celui des filtres. Par conséquent, utilisez les propriétés 3D avec modération lorsque c’est l’unité centrale qui effectue les transformations 3D. Objets de texte et mémoire Utilisez Adobe® Flash® Text Engine pour le texte en lecture seule et des objets TextFields pour le texte de saisie. Dernière mise à jour le 9/5/2012 23 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Conservation de mémoire Flash Player 10 et AIR 1.5 ont introduit un nouveau moteur de texte puissant, Adobe Flash Text Engine (FTE), qui permet de conserver la mémoire système. Ce moteur est néanmoins une API de bas niveau qui nécessite une couche ActionScript 3.0 supplémentaire, fournie dans le package flash.text.engine. Pour le texte en lecture seule, il est préférable d’utiliser Flash Text Engine, qui est peu gourmand en mémoire et offre un meilleur rendu. Les objets TextFields, quant à eux, sont plus adaptés au texte de saisie, car ils nécessitent moins de code ActionScript pour créer des comportements standard tels que la gestion de la saisie et le retour à la ligne. Voir aussi « Rendu des objets de texte » à la page 69 Modèle d’événement et rappels Envisagez d’utiliser de simples rappels plutôt que le modèle d’événement. Le modèle d’événement d’ActionScript 3.0 est fondé sur le concept de distribution d’objets. Il est orienté objet et optimisé pour la réutilisation du code. La méthode dispatchEvent() effectue une boucle dans la liste d’écouteurs et appelle la méthode du gestionnaire d’événement sur chaque objet enregistré. Ce modèle présente cependant un inconvénient : vous risquez en effet de créer un grand nombre d’objets tout au long de la vie de votre application. Imaginons que vous devez distribuer un événement à partir du scénario, pour indiquer la fin d’une séquence d’animation. Pour accomplir la notification, vous pouvez distribuer un événement à partir d’une image spécifique du scénario, comme illustré dans le code suivant : dispatchEvent( new Event ( Event.COMPLETE ) ); La ligne de code suivante permet à la classe Document d’écouter cet événement : addEventListener( Event.COMPLETE, onAnimationComplete ); Bien que cette technique soit correcte, l’utilisation du modèle d’événement natif peut être plus lente et exiger plus de mémoire qu’une fonction de rappel classique. Il est nécessaire de créer des objets d’événement et de les affecter en mémoire, ce qui ralentit les performances. Lorsque vous écoutez l’événement Event.ENTER_FRAME, par exemple, un objet d’événement est créé sur chaque image pour le gestionnaire d’événement. Les performances des objets d’affichage peuvent être particulièrement lentes, en raison des phases de capture et de propagation, ce qui se traduit par une forte sollicitation des ressources si la liste d’affichage est complexe. Dernière mise à jour le 9/5/2012 24 25 Chapitre 3 : Réduction de la charge de l’unité centrale En matière d’optimisation, il est également impératif de tenir compte de l’utilisation de l’unité centrale. L’optimisation du traitement par l’unité centrale améliore les performances et, par conséquent, la durée de vie de la batterie des périphériques mobiles. Améliorations de Flash Player 10.1 liées à l’utilisation de l’unité centrale Flash Player 10.1 propose deux nouvelles fonctions qui permettent d’économiser la puissance de traitement de l’unité centrale, à savoir la mise en pause et la reprise du contenu SWF lorsque ce dernier sort de l’écran, et la limitation du nombre d’occurrences de Flash Player sur une page. Pause, ralentissement et reprise Remarque : la fonction de pause, ralentissement et reprise n’est pas disponible sur les applications Adobe® AIR®. Pour optimiser l’utilisation de l’unité centrale et de la batterie, Flash Player 10.1 propose une nouvelle fonction relative aux occurrences inactives. Cette fonction permet de limiter l’utilisation de l’unité centrale en mettant en pause et en reprenant le fichier SWF lorsque le contenu sort de l’écran et y revient. Grâce à cette fonction, Flash Player libère autant de mémoire que possible en supprimant tout objet pouvant être recréé lorsque la lecture du contenu reprend. Est considéré comme hors écran, tout contenu qui est totalement sorti de l’écran. Deux cas de figure entraînent la sortie de l’écran du contenu SWF : • L’utilisateur fait défiler la page et le contenu SWF sort de l’écran. Dans ce cas, tout contenu audio ou vidéo en cours de lecture continue de s’exécuter mais le rendu est interrompu. Si aucun contenu audio ou vidéo n’est en cours de lecture, pour parer à toute mise en pause de la lecture ou de l’exécution d’ActionScript, définissez le paramètre HTML hasPriority sur true. Souvenez-vous cependant que, lorsque le contenu SWF est hors écran ou masqué, son rendu est mis en pause, quelle que soit la valeur du paramètre hasPriority. • L’utilisateur ouvre un onglet dans le navigateur, faisant passer à l’arrière-plan le contenu SWF. Dans ce cas, quelle que soit la valeur de la balise HTML hasPriority, le contenu SWF est ralenti ou réduit de 2 à 8 ips. La lecture audio et vidéo est arrêtée et aucun rendu n’est traité à moins que le contenu SWF ne soit à nouveau visible. Pour Flash Player 11.2 et les versions ultérieures exécutées sur des navigateurs Windows et Mac, vous pouvez utiliser l’événement ThrottleEvent dans votre application. Flash Player distribue un événement ThrottleEvent lorsqu’il interrompt, ralenti ou reprend la lecture. L’événement ThrottleEvent est un événement de diffusion, c’est-à-dire qu’il est distribué par tous les objets EventDispatcher disposant d’un écouteur de cet événement. Pour plus d’informations sur les événements de diffusion, voir la classe DisplayObject. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale Gestion des occurrences Remarque : la fonction de gestion des occurrences ne s’applique pas aux applications Adobe® AIR®. Utilisez le paramètre HTML hasPriority pour retarder le chargement des fichiers SWF hors écran. Flash Player 10.1 propose un nouveau paramètre HTML, hasPriority : <param name="hasPriority" value="true" /> Cette fonction limite le nombre d’occurrences de Flash Player démarrées sur une page, ce qui permet de conserver les ressources de l’unité centrale et de la batterie. Il s’agit d’attribuer une priorité spécifique au contenu SWF, de sorte qu’un certain contenu soit prioritaire sur une page. Prenons un exemple simple : un utilisateur explore un site Web dont la page d’index héberge trois fichiers SWF différents. Le premier est entièrement visible, le deuxième l’est partiellement et le dernier est hors écran et ne peut être atteint que par défilement. Les deux premières animations démarrent normalement mais la troisième est différée jusqu’à ce qu’elle devienne visible. C’est le comportement par défaut lorsque le paramètre hasPriority est absent ou défini sur false. Pour s’assurer du démarrage d’un fichier SWF, même s’il est hors écran, définissez le paramètre hasPriority sur true. Toutefois, quelle que soit la valeur du paramètre hasPriority, le rendu d’un fichier SWF qui n’est pas visible pour l’utilisateur est toujours mis en pause. Remarque : si les ressources de l’unité centrale sont insuffisantes, les occurrences de Flash Player ne démarrent plus automatiquement, même si le paramètre hasPriority correspond à true. Les nouvelles occurrences éventuellement créées par le biais de JavaScript après le chargement de la page ne tiennent pas compte de l’indicateur hasPriority. Tout contenu de 1 x 1 ou 0 x 0 pixel démarre, empêchant le report du lancement des fichiers SWF d’aide si le webmestre omet l’indicateur hasPriority. Il est cependant toujours possible de démarrer les fichiers SWF en cliquant dessus. Ce comportement est appelé « cliquer pour lire ». Le schéma ci-dessous illustre l’effet de la définition du paramètre hasPriority sur différentes valeurs : zone visible sur le périphérique de l’utilisateur SWF hasPriority défini sur false ou absent SWF hasPriority défini sur false ou absent SWF hasPriority défini sur false ou absent Animation SWF démarrée Animation SWF non démarrée Effets de différentes valeurs affectées au paramètre hasPriority Dernière mise à jour le 9/5/2012 26 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale zone visible sur le périphérique de l’utilisateur SWF hasPriority défini sur false ou absent SWF hasPriority défini sur false ou absent SWF hasPriority défini sur true Animation SWF démarrée Animation SWF non démarrée Effets de différentes valeurs affectées au paramètre hasPriority Mode veille Flash Player 10.1 et AIR 2.5 offrent, sur les périphériques mobiles, une nouvelle fonction qui permet d’économiser la puissance de traitement et, par conséquent, de prolonger la durée de vie des batteries. Cette fonction est liée à la fonction de rétroéclairage des périphériques mobiles. Par exemple, si un utilisateur exécutant une application mobile est interrompu et cesse d’utiliser son périphérique, le moteur d’exécution détecte le passage en mode veille du rétroéclairage. Il réduit alors la cadence à 4 images par seconde et interrompt le rendu. Pour les applications AIR, le mode veille est également activé lorsque l’application passe en arrière-plan. Le code ActionScript continue à s’exécuter en mode veille, ce qui revient à définir la propriété Stage.frameRate sur 4 images par seconde. Cependant comme l’étape de rendu est omise, l’utilisateur ne se rend pas compte de la cadence. Le choix s’est arrêté sur 4 images par seconde, et non sur zéro, pour que toutes les connexions (NetStream, Socket et NetConnection) restent ouvertes. Une cadence nulle entraînerait la fermeture de toutes les connexions ouvertes. De même, la fréquence d’actualisation est définie sur 250 ms (4 images par seconde), car c’est celle qu’utilisent de nombreux fabricants de périphériques. Cette valeur permet donc d’adapter la cadence du moteur d’exécution au réglage du périphérique. Remarque : lorsque le moteur d’exécution est en mode veille, la propriété Stage.frameRate renvoie la cadence du fichier SWF d’origine, et non 4 ips. Dernière mise à jour le 9/5/2012 27 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale Lors de la réactivation du rétroéclairage, le rendu reprend et la cadence originale est rétablie. Imaginons qu’utilisateur écoute de la musique sur un lecteur multimédia. Si l’écran passe en mode veille, le comportement du moteur d’exécution dépend du type de contenu en cours de lecture. Voici une liste des divers cas de figure possibles et des comportements correspondants du moteur d’exécution : • Le rétroéclairage passe en mode veille et le contenu en cours de lecture est de type non-A/V : le rendu est mis en pause et la cadence est définie sur 4 images par seconde. • Le rétroéclairage passe en mode veille et le contenu en cours de lecture est de type A/V : le moteur d’exécution empêche la mise en veille du rétroéclairage et rien ne change pour l’utilisateur. • Le rétroéclairage quitte le mode veille : le moteur d’exécution définit la cadence sur le paramètre correspondant du fichier SWF original et reprend le rendu. • Flash Player est mis en pause pendant la lecture de contenu A/V : Flash Player rétablit le comportement système par défaut du rétroéclairage, car la lecture du contenu A/V est interrompue. • Le périphérique mobile reçoit un appel pendant la lecture de contenu A/V : le rendu est mis en pause et la cadence est définie sur 4 images par seconde. • Le mode veille du rétroéclairage est désactivé sur un périphérique mobile : le moteur d’exécution se comporte normalement. Lorsque le rétroéclairage est mis en veille, le rendu est mis en pause et la cadence ralentit. Si bénéfique soit-elle sur la charge de l’unité centrale, cette fonction ne permet cependant pas de créer une pause réelle, comme dans une application de jeu. Remarque : aucun événement ActionScript n’est distribué lorsque le moteur d’exécution passe en mode veille ou le quitte. Figement et libération d’objets Pour figer et libérer des objets correctement, utilisez les événements REMOVED_FROM_STAGE et ADDED_TO_STAGE. Pour optimiser le code, figez et libérez systématiquement les objets. Il est important d’effectuer ces opérations pour tous les objets, et plus particulièrement les objets d’affichage. Même s’ils ne figurent plus sur la liste d’affichage et sont en attente de nettoyage, les objets d’affichage sont toujours susceptibles de solliciter fortement l’unité centrale. Il se peut, par exemple, qu’ils continuent d’utiliser Event.ENTER_FRAME. Il est donc impératif de figer et de libérer correctement les objets à l’aide des événements Event.REMOVED_FROM_STAGE et Event.ADDED_TO_STAGE. L’exemple suivant illustre un clip qui s’exécute sur la scène et communique avec le clavier : Dernière mise à jour le 9/5/2012 28 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale // Listen to keyboard events stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); // Create object to store key states var keys:Dictionary = new Dictionary(true); function keyIsDown(e:KeyboardEvent):void { // Remember that the key was pressed keys[e.keyCode] = true; if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) { runningBoy.play(); } } function keyIsUp(e:KeyboardEvent):void { // Remember that the key was released keys[e.keyCode] = false; for each (var value:Boolean in keys) if ( value ) return; runningBoy.stop(); } runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); runningBoy.stop(); var currentState:Number = runningBoy.scaleX; var speed:Number = 15; function handleMovement(e:Event):void { if (keys[Keyboard.RIGHT]) { e.currentTarget.x += speed; e.currentTarget.scaleX = currentState; } else if (keys[Keyboard.LEFT]) { e.currentTarget.x -= speed; e.currentTarget.scaleX = -currentState; } } Dernière mise à jour le 9/5/2012 29 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale Clip interagissant avec le clavier Lorsque l’utilisateur clique sur le bouton Remove (Supprimer), le clip est effacé de la liste d’affichage : // Show or remove running boy showBtn.addEventListener (MouseEvent.CLICK,showIt); removeBtn.addEventListener (MouseEvent.CLICK,removeIt); function showIt (e:MouseEvent):void { addChild (runningBoy); } function removeIt(e:MouseEvent):void { if (contains(runningBoy)) removeChild(runningBoy); } Bien que supprimé de la liste d’affichage, le clip distribue toujours l’événement Event.ENTER_FRAME. Son exécution se poursuit même si son rendu est interrompu. Pour gérer correctement cette situation, écoutez les événements appropriés et supprimez des écouteurs d’événements, afin d’éviter l’exécution de code sollicitant fortement l’unité centrale : Dernière mise à jour le 9/5/2012 30 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale // Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); function activate(e:Event):void { // Restart everything e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); } function deactivate(e:Event):void { // Freeze the running boy - consumes fewer CPU resources when not shown e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); e.currentTarget.stop(); } Lorsque l’utilisateur appuie sur le bouton Show (Afficher), le clip redémarre, écoute à nouveau les événements Event.ENTER_FRAME et le clavier le contrôle correctement. Remarque : si un objet d’affichage est supprimé de la liste d’affichage, il ne suffit pas, pour le figer, de définir sa référence sur null après sa suppression. Si le nettoyeur de mémoire ne s’exécute pas, l’objet continue de solliciter la mémoire et l’unité centrale même s’il n’est plus affiché. Pour vous assurer que l’objet sollicite le moins possible l’unité centrale, veillez à le figer complètement lors de sa suppression de la liste d’affichage. A partir de Flash Player 10 et AIR 1.5, le comportement suivant se produit également. Si la tête de lecture atteint une image vide, l’objet d’affichage est automatiquement figé même si vous n’avez pas implémenté un tel comportement. Le concept de figement est également important lors du chargement de contenu à distance à l’aide de la classe Loader. Lorsque vous utilisiez cette classe dans Flash Player 9 et AIR 1.0, il était nécessaire de figer manuellement le contenu en écoutant l’événement Event.UNLOAD distribué par l’objet LoaderInfo. Il fallait donc figer manuellement tous les objets, une tâche laborieuse. La nouvelle méthode unloadAndStop() de la classe Loader constitue une nouveauté importante dans Flash Player 10 et AIR 1.5. Elle permet en effet de décharger un fichier SWF, de figer automatiquement chaque objet du fichier et d’imposer l’exécution du nettoyeur de mémoire. Dans le code ci-après, le fichier SWF est chargé puis déchargé par le biais de la méthode unload(), qui est gourmande en ressources de traitement et exige un figement manuel : var loader:Loader = new Loader(); loader.load ( new URLRequest ( "content.swf" ) ); addChild ( loader ); stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); function unloadSWF ( e:MouseEvent ):void { // Unload the SWF file with no automatic object deactivation // All deactivation must be processed manually loader.unload(); } Il est préférable d’utiliser la méthode unloadAndStop(), qui gère le figement en natif et impose l’exécution du nettoyeur de mémoire : Dernière mise à jour le 9/5/2012 31 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale var loader:Loader = new Loader(); loader.load ( new URLRequest ( "content.swf" ) ); addChild ( loader ); stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); function unloadSWF ( e:MouseEvent ):void { // Unload the SWF file with automatic object deactivation // All deactivation is handled automatically loader.unloadAndStop(); } L’appel de la méthode unloadAndStop() entraîne les actions suivantes : • Les sons sont arrêtés. • Les écouteurs enregistrés sur le scénario principal du fichier SWF sont supprimés. • Les objets Timer sont arrêtés. • Les périphériques matériels (caméras, microphones, etc.) sont libérés. • Tous les clips sont arrêtés. • Les événements Event.ENTER_FRAME, Event.FRAME_CONSTRUCTED, Event.EXIT_FRAME, Event.ACTIVATE et Event.DEACTIVATE ne sont plus distribués. Evénements activate et deactivate Utilisez les événements Event.ACTIVATE et Event.DEACTIVATE pour détecter l’inactivité de l’arrière-plan et optimiser votre application. Deux événements (Event.ACTIVATE et Event.DEACTIVATE) peuvent vous aider à optimiser votre application de façon à ce qu’elle consomme le moins de ressources de l’unité centrale possible. Ces événements permettent de savoir quand le moteur d’exécution possède ou perd le focus. Il est ainsi possible d’optimiser le code de telle sorte qu’il réagisse aux changements de contexte. Le code suivant écoute ces deux événements et définit dynamiquement la cadence sur zéro lorsque l’application perd le focus. Par exemple, l’animation peut perdre le focus lorsque l’utilisateur sélectionne un autre onglet ou fait passer l’application en arrière-plan : Dernière mise à jour le 9/5/2012 32 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale var originalFrameRate:uint = stage.frameRate; var standbyFrameRate:uint = 0; stage.addEventListener ( Event.ACTIVATE, onActivate ); stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); function onActivate ( e:Event ):void { // restore original frame rate stage.frameRate = originalFrameRate; } function onDeactivate ( e:Event ):void { // set frame rate to 0 stage.frameRate = standbyFrameRate; } Lorsque l’application retrouve le focus, la cadence reprend sa valeur d’origine. Plutôt que de modifier dynamiquement la cadence, vous pouvez effectuer d’autres optimisations, par exemple le figement ou la libération d’objets. Les événements activate et deactivate permettent de mettre en oeuvre un mécanisme similaire à la fonction « pause et reprise » parfois disponible sur les périphériques mobiles et les ordinateurs miniportables. Voir aussi « Cadence de l’application » à la page 54 « Figement et libération d’objets » à la page 28 Interactions de la souris Dans la mesure du possible, désactivez les interactions de la souris. Lors de l’utilisation d’un objet interactif, tel qu’un objet MovieClip ou Sprite, le moteur d’exécution exécute du code natif pour détecter et gérer les interactions de la souris. Lorsque de nombreux objets interactifs sont affichés, ce processus de détection peut solliciter fortement l’unité centrale, surtout si les objets se chevauchent. Pour parer à cet écueil, le plus simple consiste à désactiver les interactions de la souris sur les objets pour lesquels elles ne sont pas nécessaires. Le code suivant illustre l’utilisation des propriétés mouseEnabled et mouseChildren : Dernière mise à jour le 9/5/2012 33 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale // Disable any mouse interaction with this InteractiveObject myInteractiveObject.mouseEnabled = false; const MAX_NUM:int = 10; // Create a container for the InteractiveObjects var container:Sprite = new Sprite(); for ( var i:int = 0; i< MAX_NUM; i++ ) { // Add InteractiveObject to the container container.addChild( new Sprite() ); } // Disable any mouse interaction on all the children container.mouseChildren = false; Lorsque cela est possible, désactivez les interactions de la souris : votre application fait alors une utilisation moins intensive de l’unité centrale, d’où une consommation réduite de la batterie sur les périphériques mobiles. Minuteurs et événements ENTER_FRAME Optez pour des minuteurs ou des événements ENTER_FRAME, selon que le contenu est animé ou non. Lorsque le contenu n’est pas animé et s’exécute pendant une longue période, les minuteurs sont préférables aux événements Event.ENTER_FRAME. Dans ActionScript 3.0, il est possible d’appeler une fonction à intervalles réguliers de deux manières. La première repose sur l’utilisation de l’événement Event.ENTER_FRAME distribué par les objets d’affichage (DisplayObject). La seconde est fondée sur l’utilisation d’un minuteur. Les développeurs ActionScript privilégient fréquemment l’utilisation de l’événement ENTER_FRAME. L’événement ENTER_FRAME est distribué sur chaque image. La fréquence d’appel de la fonction est donc relative à la cadence en cours. La cadence est accessible par le biais de la propriété Stage.frameRate. Il est cependant préférable, dans certains cas, de faire appel à un minuteur plutôt qu’à l’événement ENTER_FRAME. C’est le cas, par exemple, si vous ne définissez pas d’animation mais souhaitez appeler le code à fréquence régulière. Un minuteur peut se comporter de manière similaire à un événement ENTER_FRAME, mais il permet de distribuer un événement sans être tributaire de la cadence. Ce comportement peut permettre une optimisation sensible. Prenons l’exemple d’un lecteur vidéo. Dans ce cas, une cadence élevée n’est pas indispensable, car seuls les contrôles de l’application se déplacent. Remarque : la cadence n’affecte pas la vidéo parce que celle-ci n’est pas intégrée au scénario. La vidéo est chargée dynamiquement par téléchargement progressif ou diffusion en continu. Dans cet exemple, la cadence est réglée sur une valeur faible, 10 ips. Le minuteur met à jour les contrôles, à raison de une mise à jour par seconde. Le taux de mise à jour plus élevé est possible grâce à la méthode updateAfterEvent(), qui est disponible sur l’objet TimerEvent. Cette méthode impose la mise à jour de l’écran chaque fois que le minuteur distribue un événement, si besoin est. Le code suivant illustre ce concept : Dernière mise à jour le 9/5/2012 34 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale // Use a low frame rate for the application stage.frameRate = 10; // Choose one update per second var updateInterval:int = 1000; var myTimer:Timer = new Timer(updateInterval,0); myTimer.start(); myTimer.addEventListener( TimerEvent.TIMER, updateControls ); function updateControls( e:TimerEvent ):void { // Update controls here // Force the controls to be updated on screen e.updateAfterEvent(); } L’appel de la méthode updateAfterEvent() ne modifie pas la cadence. Cette méthode force simplement le moteur d’exécution à mettre à jour le contenu de l’écran qui a changé. Le scénario s’exécute toujours à 10 ips. Souvenez-vous cependant que les minuteurs et les événements ENTER_FRAME ne sont pas d’une précision à toute épreuve sur les périphériques dont les performances sont médiocres ou si les fonctions de gestionnaire d’événement contiennent du code exigeant un traitement intensif. A l’instar de la cadence des fichiers SWF, le taux de mise à jour du minuteur peut varier en fonction des circonstances. Définissez un minimum d’objets Timer et de gestionnaires enterFrame enregistrés dans l’application. Sur chaque image, le moteur d’exécution distribue un événement enterFrame à chaque objet figurant sur sa liste d’affichage. Vous pouvez enregistrer des écouteurs pour l’événement enterFrame sur plusieurs objets d’affichage mais cela signifie que le volume de code exécuté sur chaque image est plus élevé. Envisagez plutôt de définir un gestionnaire enterFrame centralisé unique qui exécute tout le code destiné à chaque image. En centralisant ce code, il est plus facile de gérer tout le code qui s’exécute fréquemment. De même, si vous utilisez plusieurs objets Timer, la création et la distribution des événements correspondants se traduisent par une augmentation du temps système nécessaire. Si vous devez déclencher différentes opérations à différents intervalles, nous vous suggérons de procéder comme suit, au choix : • Utilisez un minimum d’objets Timer et regroupez les opérations en fonction de leur fréquence d’exécution.. Définissez, par exemple, un objet Timer se déclenchant toutes les 100 millisecondes pour les opérations fréquentes et un autre, réglé sur 2000 millisecondes, pour les opérations moins fréquentes ou exécutées en arrière-plan. • Utilisez un seul objet Timer et déclenchez les opérations à des fréquences consistant en des multiples de la propriété delay de l’objet. Supposons, par exemple, que vous souhaitiez exécuter certaines opérations toutes les 100 millisecondes et d’autres toutes les 200 millisecondes. A cet effet, créez un seul objet Timer et définissez sa propriété delay sur 100 millisecondes. Dans le gestionnaire d’événement timer, ajoutez une instruction conditionnelle qui exécute une fois sur deux seulement les opérations à répéter toutes les 200 millisecondes. L’exemple suivant illustre cette technique : Dernière mise à jour le 9/5/2012 35 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Réduction de la charge de l’unité centrale var timer:Timer = new Timer(100); timer.addEventListener(TimerEvent.Timer, timerHandler); timer.start(); var offCycle:Boolean = true; function timerHandler(event:TimerEvent):void { // Do things that happen every 100 ms if (!offCycle) { // Do things that happen every 200 ms } offCycle = !offCycle; } Arrêtez les objets Timer lorsqu’ils ne sont pas utilisés. Si le gestionnaire d’événement timer d’un objet Timer n’exécute des opérations que dans certaines conditions, appelez la méthode stop() de l’objet lorsqu’aucune des conditions n’est vérifiée. Dans un événement enterFrame ou des gestionnaires Timer, évitez autant que possible d’apporter à l’apparence de l’objet d’affichage des modifications entraînant l’actualisation de l’écran. Sur chaque image, la phase de rendu retrace la zone de la scène qui a changé pendant l’image. Si cette zone est de grande taille ou si elle est petite mais contient des objets d’affichage nombreux ou complexes, le temps nécessaire pour le rendu augmente. La fonction « Afficher les zones de retraçage » de la version de débogage de Flash Player ou AIR permet de déterminer la quantité de retraçage nécessaire. Pour plus d’informations sur l’amélioration des performances des actions répétées, voir l’article suivant : • Writing well-behaved, efficient, AIR applications (Article et exemple d’application d’Arno Gourdol ; disponibles en anglais uniquement.) Voir aussi « Isolation de comportements » à la page 66 Interpolations Evitez d’utiliser des interpolations pour économiser la puissance de traitement de l’unité centrale, la mémoire et la vie de la batterie. Les concepteurs et développeurs qui produisent du contenu pour Flash sur le bureau ont tendance à être grands consommateurs d’interpolations de mouvement dans leurs applications. Lorsque vous créez du contenu destiné à des périphériques mobiles, limitez autant que possible l’utilisation des interpolations de mouvement afin que le contenu s’exécute plus rapidement sur les périphériques bas de gamme. Dernière mise à jour le 9/5/2012 36 37 Chapitre 4 : Performances d’ActionScript 3.0 Comparaison des classes Vector et Array Dans la mesure du possible, faites appel à la classe Vector plutôt qu’à la classe Array. La classe Vector autorise des accès en lecture et écriture plus rapides que la classe Array. Un simple test de performances démontre les avantages que présente la classe Vector par rapport à la classe Array. Le code suivant illustre un test de performances concernant la classe Array : var coordinates:Array = new Array(); var started:Number = getTimer(); for (var i:int = 0; i< 300000; i++) { coordinates[i] = Math.random()*1024; } trace(getTimer() - started); // output: 107 Le code suivant illustre un test de performances concernant la classe Vector : var coordinates:Vector.<Number> = new Vector.<Number>(); var started:Number = getTimer(); for (var i:int = 0; i< 300000; i++) { coordinates[i] = Math.random()*1024; } trace(getTimer() - started); // output: 72 Il est possible d’optimiser encore plus l’exemple en attribuant une longueur précise fixe au vecteur : // Specify a fixed length and initialize its length var coordinates:Vector.<Number> = new Vector.<Number>(300000, true); var started:Number = getTimer(); for (var i:int = 0; i< 300000; i++) { coordinates[i] = Math.random()*1024; } trace(getTimer() - started); // output: 48 Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 Si la taille du vecteur n’est pas définie à l’avance, elle augmente lorsque le vecteur n’a plus de place. A chaque augmentation de la taille du vecteur, un nouveau bloc de mémoire est affecté. Le contenu actif du vecteur est copié dans le nouveau bloc de mémoire. Cette affectation de mémoire supplémentaire et la copie des données ont une incidence sur les performances. Dans le code ci-dessus, la taille initiale du vecteur est définie en vue d’optimiser les performances. Ce code n’est cependant pas optimisé à des fins de maintenabilité. Pour améliorer sa maintenabilité, placez la valeur réutilisée dans une constante : // Store the reused value to maintain code easily const MAX_NUM:int = 300000; var coordinates:Vector.<Number> = new Vector.<Number>(MAX_NUM, true); var started:Number = getTimer(); for (var i:int = 0; i< MAX_NUM; i++) { coordinates[i] = Math.random()*1024; } trace(getTimer() - started); // output: 47 Dans la mesure du possible, utilisez les API de l’objet Vector, car leur exécution sera probablement plus rapide. API de dessin L’API de dessin accélère l’exécution du code. Flash Player 10 et AIR 1.5 intègrent une nouvelle API de dessin visant à optimiser les performances d’exécution du code. Si elle n’assure pas de meilleures performances de rendu, cette API permet cependant de réduire le nombre de lignes de code nécessaires, et cette réduction se traduit par une optimisation des performances d’exécution d’ActionScript. La nouvelle API de dessin comporte les méthodes suivantes : • drawPath() • drawGraphicsData() • drawTriangles() Remarque : nous ne nous attarderons pas ici sur la méthode drawTriangles(), qui concerne le 3D. Cette méthode permet toutefois d’optimiser les performances d’ActionScript, car elle gère le mappage des textures en natif. Le code ci-dessous appelle explicitement la méthode appropriée pour chaque ligne à tracer : Dernière mise à jour le 9/5/2012 38 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 var container:Shape = new Shape(); container.graphics.beginFill(0x442299); var coords:Vector.<Number> = Vector.<Number>([132, 20, 46, 254, 244, 100, 20, 98, 218, 254]); container.graphics.moveTo container.graphics.lineTo container.graphics.lineTo container.graphics.lineTo container.graphics.lineTo ( ( ( ( ( coords[0], coords[2], coords[4], coords[6], coords[8], coords[1] coords[3] coords[5] coords[7] coords[9] ); ); ); ); ); addChild( container ); Le code suivant s’exécute plus rapidement que celui de l’exemple précédent, car il contient un moins grand nombre de lignes. Plus le tracé est complexe et meilleures sont les performances résultant de l’utilisation de la méthode drawPath() : var container:Shape = new Shape(); container.graphics.beginFill(0x442299); var commands:Vector.<int> = Vector.<int>([1,2,2,2,2]); var coords:Vector.<Number> = Vector.<Number>([132, 20, 46, 254, 244, 100, 20, 98, 218, 254]); container.graphics.drawPath(commands, coords); addChild( container ); La méthode drawGraphicsData() offre des possibilités d’optimisation similaires. Capture et propagation d’événement La capture et la propagation d’événement permettent de réduire le nombre de gestionnaires d’événement. Le modèle d’événement d’ActionScript 3.0 a introduit les concepts de capture et de propagation d’événement. En propageant un événement, vous pouvez optimiser le temps d’exécution du code ActionScript. Vous pouvez enregistrer un gestionnaire d’événement sur un objet, plutôt que sur plusieurs objets, pour améliorer les performances. Imaginons, par exemple, un jeu dans lequel l’utilisateur doit cliquer sur des pommes aussi vite que possible pour les détruire. Chaque pomme sur laquelle clique l’utilisateur est effacée et des points sont ajoutés au score de celui-ci. Pour écouter l’événement MouseEvent.CLICK distribué par chaque pomme, vous pourriez être tenté de rédiger le code suivant : Dernière mise à jour le 9/5/2012 39 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 const MAX_NUM:int = 10; var sceneWidth:int = stage.stageWidth; var sceneHeight:int = stage.stageHeight; var currentApple:InteractiveObject; var currentAppleClicked:InteractiveObject; for ( var i:int = 0; i< MAX_NUM; i++ ) { currentApple = new Apple(); currentApple.x = Math.random()*sceneWidth; currentApple.y = Math.random()*sceneHeight; addChild ( currentApple ); // Listen to the MouseEvent.CLICK event currentApple.addEventListener ( MouseEvent.CLICK, onAppleClick ); } function onAppleClick ( e:MouseEvent ):void { currentAppleClicked = e.currentTarget as InteractiveObject; currentAppleClicked.removeEventListener(MouseEvent.CLICK, onAppleClick ); removeChild ( currentAppleClicked ); } Le code appelle la méthode addEventListener() sur chaque occurrence d’Apple. Il supprime également chaque écouteur lorsque l’utilisateur clique sur une pomme, à l’aide de la méthode removeEventListener(). Cependant, le modèle d’événement d’ActionScript 3.0 propose une phase de capture et de propagation de certains événements, vous permettant d’écouter ceux-ci à partir d’un objet interactif (InteractiveObject) parent. Il est donc possible d’optimiser le code ci-dessus et de réduire le nombre d’appels des méthodes addEventListener() et removeEventListener(). Le code suivant utilise la phase de capture pour écouter les événements en provenance de l’objet parent : const MAX_NUM:int = 10; var sceneWidth:int = stage.stageWidth; var sceneHeight:int = stage.stageHeight; var currentApple:InteractiveObject; var currentAppleClicked:InteractiveObject; var container:Sprite = new Sprite(); addChild ( container ); // Listen to the MouseEvent.CLICK on the apple's parent // Passing true as third parameter catches the event during its capture phase container.addEventListener ( MouseEvent.CLICK, onAppleClick, true ); for ( var i:int = 0; i< MAX_NUM; i++ ) { currentApple = new Apple(); currentApple.x = Math.random()*sceneWidth; currentApple.y = Math.random()*sceneHeight; container.addChild ( currentApple ); } function onAppleClick ( e:MouseEvent ):void { currentAppleClicked = e.target as InteractiveObject; container.removeChild ( currentAppleClicked ); } Dernière mise à jour le 9/5/2012 40 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 Le code est simplifié et grandement optimisé ; il appelle la méthode addEventListener() une seule fois sur le conteneur parent. Les écouteurs n’étant plus enregistrés sur les occurrences d’Apple, il est inutile de les supprimer lorsque l’utilisateur clique sur une pomme. Il est possible d’optimiser encore plus le gestionnaire onAppleClick(), en arrêtant la propagation de l’événement, ce qui l’empêche d’aller plus loin : function onAppleClick ( e:MouseEvent ):void { e.stopPropagation(); currentAppleClicked = e.target as InteractiveObject; container.removeChild ( currentAppleClicked ); } Vous pouvez aussi utiliser la phase de propagation pour capturer l’événement en transmettant la valeur false en tant que troisième paramètre à la méthode addEventListener() : // Listen to the MouseEvent.CLICK on apple's parent // Passing false as third parameter catches the event during its bubbling phase container.addEventListener ( MouseEvent.CLICK, onAppleClick, false ); La valeur par défaut du paramètre de la phase de capture est false ; vous pouvez donc l’omettre : container.addEventListener ( MouseEvent.CLICK, onAppleClick ); Utilisation des pixels Pour peindre des pixels, utilisez la méthode setVector(). Lors de la peinture de pixels, il est possible de réaliser des optimisations simples à l’aide des méthodes appropriées de la classe BitmapData. Pour peindre rapidement des pixels, vous pouvez utiliser la méthode setVector() : // Image dimensions var wdth:int = 200; var hght:int = 200; var total:int = wdth*hght; // Pixel colors Vector var pixels:Vector.<uint> = new Vector.<uint>(total, true); for ( var i:int = 0; i< total; i++ ) { // Store the color of each pixel pixels[i] = Math.random()*0xFFFFFF; } // Create a non-transparent BitmapData object var myImage:BitmapData = new BitmapData ( wdth, hght, false ); var imageContainer:Bitmap = new Bitmap ( myImage ); // Paint the pixels myImage.setVector ( myImage.rect, pixels ); addChild ( imageContainer ); Lorsque vous utilisez des méthodes lentes, telles que setPixel() ou setPixel32(), faites appel aux méthodes lock() et unlock() pour accélérer le processus. Dans le code suivant, les méthodes lock() et unlock() ont pour objet d’accélérer les performances : Dernière mise à jour le 9/5/2012 41 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 var var var var buffer:BitmapData = new BitmapData(200,200,true,0xFFFFFFFF); bitmapContainer:Bitmap = new Bitmap(buffer); positionX:int; positionY:int; // Lock update buffer.lock(); var starting:Number=getTimer(); for (var i:int = 0; i<2000000; i++) { // Random positions positionX = Math.random()*200; positionY = Math.random()*200; // 40% transparent pixels buffer.setPixel32( positionX, positionY, 0x66990000 ); } // Unlock update buffer.unlock(); addChild( bitmapContainer ); trace( getTimer () - starting ); // output : 670 La méthode lock() de la classe BitmapData verrouille une image et empêche la mise à jour des objets qui y font référence lors de la modification de l’objet BitmapData. Si un objet Bitmap fait référence à un objet BitmapData, par exemple, vous pouvez verrouiller ce dernier, le modifier et le déverrouiller. L’objet Bitmap n’est pas modifié tant que l’objet BitmapData n’est pas déverrouillé. Pour améliorer les performances, utilisez cette méthode en conjonction avec la méthode unlock() avant et après des appels répétés de la méthode setPixel() ou setPixel32(). L’appel de lock() et unlock() empêche les mises à jour superflues de l’écran. Remarque : lors du traitement des pixels d’un bitmap ne figurant pas sur la liste d’affichage (double mise en mémoire tampon), cette technique n’améliore pas toujours les performances. Si un objet bitmap ne fait pas référence à la mémoire tampon de bitmap, l’utilisation de lock()et unlock() n’apporte aucune amélioration des performances. Flash Player détecte que la mémoire tampon n’est pas référencée et le bitmap n’est pas rendu à l’écran. Les méthodes faisant l’objet d’une itération sur les pixels, telles que getPixel(), getPixel32(), setPixel() et setPixel32(), sont souvent lentes, surtout sur les périphériques mobiles. Si possible, utilisez des méthodes qui extraient tous les pixels en une seule fois. Pour lire des pixels, utilisez la méthode getVector(), qui est plus rapide que la méthode getPixels(). Dans la mesure du possible, utilisez également des API qui s’appuient sur des objets Vector car, selon toute probabilité, leur exécution sera plus rapide. Expressions régulières Utilisez des méthodes de la classe String, telles que indexOf(), substr() ou substring(), plutôt que des expressions régulières pour définir des opérations de recherche et d’extraction de chaînes de base. Il est possible d’exécuter certaines opérations à l’aide d’une expression régulière ou des méthodes de la classe String. Pour déterminer si une chaîne en contient une autre, par exemple, vous pouvez utiliser la méthode String.indexOf() ou une expression régulière. Cependant, lorsqu’une méthode de la classe String est disponible, elle s’exécute plus rapidement que l’expression régulière équivalente et n’exige pas la création d’un autre objet. Dernière mise à jour le 9/5/2012 42 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 Utilisez un groupe autre que capture (“(?:xxxx)”) plutôt qu’un groupe (“(xxxx)”) dans une expression régulière pour regrouper les éléments sans isoler le contenu du groupe dans le résultat. Dans les expressions régulières modérément complexes, vous regroupez souvent des parties de l’expression. Dans le modèle d’expression régulière suivant, par exemple, les parenthèses créent un groupe autour du texte « ab ». Le quantificateur « + » s’applique donc au groupe et non à un seul caractère : /(ab)+/ Le contenu de chaque groupe est « capturé », par défaut. Les données résultant de l’exécution de l’expression régulière peuvent contenir le contenu de chaque groupe du modèle. La capture de ces résultats de groupe prend plus longtemps et exige plus de mémoire, car des objets destinés à les contenir sont créés. Une autre syntaxe consiste à insérer les signes point d’interrogation et deux-points après la parenthèse ouvrante pour utiliser des groupes autres que capture. Cette syntaxe spécifie que les caractères se comportent comme un groupe mais ne sont pas capturés dans le résultat : /(?:ab)+/ La syntaxe de groupes autres que capture est plus rapide et nécessite moins de mémoire que la syntaxe de groupes standard. Si une expression régulière ralentit l’exécution du code, envisagez de lui en substituer une autre. Il est parfois possible d’utiliser plusieurs modèles d’expression régulière pour tester ou identifier un même modèle de texte. Pour diverses raisons, certains modèles s’exécutent plus rapidement que d’autres. Si vous constatez qu’une expression régulière ralentit l’exécution du code plus qu’il n’est nécessaire, définissez d’autres modèles d’expression régulière permettant d’arriver au même résultat. Testez-les pour identifier le plus rapide. Optimisations diverses Pour un objet TextField, utilisez la méthode appendText() plutôt que l’opérateur +=. Lorsque vous utilisez la propriété text de la classe TextField, préférez la méthode appendText() à l’opérateur +=. Cette méthode garantit de meilleures performances. Le code suivant, par exemple, utilise l’opérateur += et il faut 1 120 ms pour exécuter la boucle : addChild ( myTextField ); myTextField.autoSize = TextFieldAutoSize.LEFT; var started:Number = getTimer(); for (var i:int = 0; i< 1500; i++ ) { myTextField.text += "ActionScript 3"; } trace( getTimer() - started ); // output : 1120 Dans l’exemple ci-dessous, nous avons remplacé l’opérateur += par la méthode appendText() : Dernière mise à jour le 9/5/2012 43 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 var myTextField:TextField = new TextField(); addChild ( myTextField ); myTextField.autoSize = TextFieldAutoSize.LEFT; var started:Number = getTimer(); for (var i:int = 0; i< 1500; i++ ) { myTextField.appendText ( "ActionScript 3" ); } trace( getTimer() - started ); // output : 847 Le code s’exécute à présent en 847 ms. Si possible, mettez à jour les champs de texte en dehors des boucles. Il est possible d’optimiser encore plus ce code par le biais d’une technique simple. La mise à jour du champ de texte dans chaque boucle met trop à contribution les ressources de traitement internes. Il suffit de concaténer une chaîne et de l’attribuer à la valeur du champ de texte en dehors de la boucle pour réduire considérablement la durée d’exécution du code, qui correspond maintenant à 2 ms : var myTextField:TextField = new TextField(); addChild ( myTextField ); myTextField.autoSize = TextFieldAutoSize.LEFT; var started:Number = getTimer(); var content:String = myTextField.text; for (var i:int = 0; i< 1500; i++ ) { content += "ActionScript 3"; } myTextField.text = content; trace( getTimer() - started ); // output : 2 Lors de l’utilisation de texte HTML, la première technique est tellement lente qu’elle renvoie parfois une exception Timeout dans Flash Player, dans certaines situations. Ce peut être le cas, par exemple, lorsque le matériel sous-jacent est trop lent. Remarque : Adobe® AIR® ne renvoie pas cette exception. Dernière mise à jour le 9/5/2012 44 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 var myTextField:TextField = new TextField(); addChild ( myTextField ); myTextField.autoSize = TextFieldAutoSize.LEFT; var started:Number = getTimer(); for (var i:int = 0; i< 1500; i++ ) { myTextField.htmlText += "ActionScript <b>2</b>"; } trace( getTimer() - started ); Attribuez une valeur à une chaîne en dehors de la boucle et le code s’exécute en 29 ms seulement : var myTextField:TextField = new TextField(); addChild ( myTextField ); myTextField.autoSize = TextFieldAutoSize.LEFT; var started:Number = getTimer(); var content:String = myTextField.htmlText; for (var i:int = 0; i< 1500; i++ ) { content += "<b>ActionScript<b> 3"; } myTextField.htmlText = content; trace ( getTimer() - started ); // output : 29 Remarque : dans Flash Player 10.1 et AIR 2.5, la classe String a été améliorée de façon à ce que les chaînes soient moins gourmandes en mémoire. Evitez si possible d’utiliser l’opérateur crochet. L’utilisation de l’opérateur crochet peut ralentir les performances. Pour éviter de vous en servir, vous pouvez stocker votre référence dans une variable locale. Le code suivant illustre une utilisation peu performante de l’opérateur crochet : Dernière mise à jour le 9/5/2012 45 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 var lng:int = 5000; var arraySprite:Vector.<Sprite> = new Vector.<Sprite>(lng, true); var i:int; for ( i = 0; i< lng; i++ ) { arraySprite[i] = new Sprite(); } var started:Number = getTimer(); for ( i = 0; i< lng; i++ ) { arraySprite[i].x = Math.random()*stage.stageWidth; arraySprite[i].y = Math.random()*stage.stageHeight; arraySprite[i].alpha = Math.random(); arraySprite[i].rotation = Math.random()*360; } trace( getTimer() - started ); // output : 16 La version optimisée de cet exemple, ci-dessous, contient moins d’opérateurs crochet : var lng:int = 5000; var arraySprite:Vector.<Sprite> = new Vector.<Sprite>(lng, true); var i:int; for ( i = 0; i< lng; i++ ) { arraySprite[i] = new Sprite(); } var started:Number = getTimer(); var currentSprite:Sprite; for ( i = 0; i< lng; i++ ) { currentSprite = arraySprite[i]; currentSprite.x = Math.random()*stage.stageWidth; currentSprite.y = Math.random()*stage.stageHeight; currentSprite.alpha = Math.random(); currentSprite.rotation = Math.random()*360; } trace( getTimer() - started ); // output : 9 Placez le code en ligne, si possible, pour réduire le nombre d’appels de fonctions qu’il contient. Les appels de fonctions sont gourmands en ressources. Réduisez autant que faire se peut le nombre de ces appels en plaçant le code en ligne. Ce faisant, vous optimiserez vraiment les performances. Tenez néanmoins compte du fait qu’il peut être plus difficile de réutiliser du code en ligne et que la taille du fichier SWF risque d’augmenter. Il est facile de déplacer en ligne des appels de fonctions tels que les méthodes de la classe Math. Le code suivant s’appuie sur la méthode Math.abs() pour calculer des valeurs absolues : Dernière mise à jour le 9/5/2012 46 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 const MAX_NUM:int = 500000; var arrayValues:Vector.<Number>=new Vector.<Number>(MAX_NUM,true); var i:int; for (i = 0; i< MAX_NUM; i++) { arrayValues[i] = Math.random()-Math.random(); } var started:Number = getTimer(); var currentValue:Number; for (i = 0; i< MAX_NUM; i++) { currentValue = arrayValues[i]; arrayValues[i] = Math.abs ( currentValue ); } trace( getTimer() - started ); // output : 70 Vous pouvez réaliser manuellement le calcul effectué par Math.abs() et le placer en ligne : const MAX_NUM:int = 500000; var arrayValues:Vector.<Number>=new Vector.<Number>(MAX_NUM,true); var i:int; for (i = 0; i< MAX_NUM; i++) { arrayValues[i] = Math.random()-Math.random(); } var started:Number = getTimer(); var currentValue:Number; for (i = 0; i< MAX_NUM; i++) { currentValue = arrayValues[i]; arrayValues[i] = currentValue > 0 ? currentValue : -currentValue; } trace( getTimer() - started ); // output : 15 En conséquence du déplacement en ligne de l’appel de fonction, le code est plus de quatre fois plus rapide. Cette technique est préconisée dans de nombreux cas, mais pensez aux effets possibles sur la réutilisation et la maintenabilité du code. Remarque : la taille du code a une grande incidence sur l’exécution globale du lecteur. Si l’application comprend beaucoup de code ActionScript, il faut longtemps à la machine virtuelle pour vérifier le code et effectuer une compilation juste à temps (JIT). Les recherches de propriétés peuvent être lentes, en raison de la complexité accrue des hiérarchies d’héritage et parce que les mémoires cache ont tendance à être plus sollicitées. Pour réduire la taille du code, évitez d’utiliser la structure Adobe® Flex®, la bibliothèque de structures TLF ou toute bibliothèque ActionScript tierce de grande taille. Evitez également les boucles d’évaluation des instructions. Dernière mise à jour le 9/5/2012 47 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances d’ActionScript 3.0 Une autre mesure d’optimisation consiste à ne pas évaluer une instruction dans une boucle. Le code suivant effectue des itérations sur un tableau mais il n’est pas optimisé, car la longueur du tableau est évaluée à chaque fois : for (var i:int = 0; i< myArray.length; i++) { } Il est préférable de stocker la valeur et de la réutiliser : var lng:int = myArray.length; for (var i:int = 0; i< lng; i++) { } Utilisez l’ordre inverse dans les boucles while. Une boucle while en ordre inverse est beaucoup plus rapide : var i:int = myArray.length; while (--i > -1) { } Ces astuces proposent quelques techniques d’optimisation d’ActionScript et illustrent l’impact d’une simple ligne de code sur les performances et la mémoire. Il en existe toutefois beaucoup d’autres. Pour plus d’informations, voir le lien suivant : http://www.rozengain.com/blog/2007/05/01/some-actionscript-30-optimizations/ (disponible en anglais uniquement). Dernière mise à jour le 9/5/2012 48 49 Chapitre 5 : Performances de rendu Options de retraçage Utilisez toujours l’option Afficher les zones de retraçage lors de la création du projet. Pour améliorer le rendu, il est important d’utiliser l’option Afficher les zones de retraçage lors de la création du projet. Cette option permet d’afficher les zones dont Flash Player effectue le rendu et qu’il traite. Pour activer cette option, sélectionnez Afficher les zones de retraçage dans le menu contextuel de la version de débogage de Flash Player. Remarque : l’option Afficher les zones de retraçage n’est pas disponible dans Adobe AIR ou dans la version commerciale de Flash Player. (Dans Adobe AIR, le menu contextuel est disponible uniquement dans les applications de bureau ; il ne comporte aucune option intégrée ou standard semblable à Afficher les zones de retraçage.) L’image suivante illustre un objet MovieClip animé simple sur le scénario, l’option Afficher les zones de retraçage étant activée : Option Afficher les zones de retraçage activée Vous pouvez également activer cette option par programmation à l’aide de la méthode flash.profiler.showRedrawRegions() : // Enable Show Redraw Regions // Blue color is used to show redrawn regions flash.profiler.showRedrawRegions ( true, 0x0000FF ); Dans les applications Adobe AIR, cette méthode est l’unique moyen d’activer l’option Afficher les zones de retraçage. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Utilisez l’option Afficher les zones de retraçage pour identifier les opportunités d’optimisation. N’oubliez pas que, même s’ils ne sont pas affichés, les objets d’affichage consomment néanmoins de nombreuses ressources de l’unité centrale, car leur rendu est toujours effectué. Ce concept est illustré ci-dessous. Une forme vectorielle noire couvre le personnage animé qui court. L’image montre que l’objet d’affichage n’a pas été supprimé de la liste d’affichage et est toujours rendu, d’où un gaspillage des ressources de l’unité centrale : Régions retracées Pour améliorer les performances, définissez la propriété visible du personnage animé masqué sur false ou supprimez-la de la liste d’affichage. Vous devez aussi arrêter son scénario. Ces mesures garantissent que l’objet d’affichage est figé et sollicite l’unité centrale au minimum. Pensez à utiliser l’option Afficher les zones de retraçage pendant tout le cycle de développement. Grâce à elle, vous ne découvrirez pas en fin de projet des zones de retraçage superflues et des zones d’optimisation potentielles dont vous ne vous étiez pas rendu compte. Voir aussi « Figement et libération d’objets » à la page 28 Contenu hors-scène Evitez de placer du contenu hors de la scène. Contentez-vous de placer les objets sur la liste d’affichage, lorsque cela est nécessaire. Dans la mesure du possible, essayez de ne pas placer de contenu graphique hors de la scène. Les concepteurs et les développeurs ont pour habitude de placer les éléments hors de la scène pour réutiliser les actifs pendant la durée de vie de l’application. La figure suivante illustre cette technique courante : Dernière mise à jour le 9/5/2012 50 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Contenu hors-scène Même si les éléments hors-scène ne sont pas visibles à l’écran et ne sont pas rendus, ils restent néanmoins présents dans la liste d’affichage. Le moteur d’exécution continue de soumettre ces éléments à des tests internes pour vérifier qu’ils sont toujours hors-scène et que l’utilisateur n’interagit pas avec eux. Vous devez donc éviter, dans la mesure du possible, de placer des objets hors de la scène en vous contentant de les supprimer de la liste d’affichage. Qualité des clips Utilisez le paramètre de qualité de scène approprié pour améliorer le rendu. Dernière mise à jour le 9/5/2012 51 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Lorsque vous développez du contenu pour des périphériques mobiles dont l’écran est de petite taille, tels que des téléphones, la qualité d’image est moins importante que pour les applications de bureau. Le choix du paramètre de qualité de scène adapté peut améliorer les performances de rendu. Les paramètres disponibles sont les suivants : • StageQuality.LOW : favorise la vitesse de lecture par rapport à l’aspect et n’utilise pas l’anticrènelage. Ce paramètre n’est pas pris en charge dans la version de bureau d’Adobe AIR ou dans Adobe AIR pour TV. • StageQuality.MEDIUM : applique un certain degré d’anticrènelage, mais ne lisse pas les bitmaps mis à l’échelle. Cette valeur est la valeur par défaut d’AIR sur les périphériques mobiles ; elle n’est pas prise en charge dans les versions AIR de bureau ou dans AIR pour TV. • StageQuality.HIGH (paramètre par défaut sur le bureau) : favorise l’aspect par rapport à la vitesse de lecture et applique systématiquement l’anticrènelage. Si le fichier SWF ne contient aucune animation, les bitmaps mis à l’échelle sont lissés ; dans le cas contraire, ils ne le sont pas. • StageQuality.BEST : assure la meilleure qualité d’affichage et ne tient pas compte de la vitesse de lecture. Toute la sortie est anticrènelée et les bitmaps mis à l’échelle sont toujours lissés. StageQuality.MEDIUM permet généralement d’obtenir une qualité adéquate pour les applications sur les périphériques mobiles. Dans certains cas, il suffit même d’utiliser StageQuality.LOW. Depuis Flash Player 8, il est possible d’effectuer un rendu exact du texte anticrènelé, même si vous utilisez le paramètre de qualité de scène LOW. Remarque : sur certains périphériques mobiles, même si la qualité est définie sur HIGH, MEDIUM est utilisée pour obtenir de meilleures performances dans les applications Flash Player. Le paramètre de qualité HIGH fait peu de différence, car les écrans des périphériques mobiles utilisent généralement une résolution plus élevée. (La résolution peut varier en fonction du périphérique.) Dans l’illustration ci-après, le paramètre de qualité de scène est défini sur MEDIUM et le rendu du texte sur Anticrènelage pour l’animation : Qualité de scène définie sur MEDIUM et rendu du texte sur Anticrènelage pour l’animation Le paramètre de qualité de scène affecte la qualité du texte, car le paramètre de rendu de texte approprié n’est pas utilisé. Le moteur d’exécution permet de définir le rendu du texte sur Anticrènelage pour la lisibilité. Grâce à elle, la qualité du texte (anticrènelé) reste parfaite quelle que soit la valeur du paramètre de qualité de scène : Dernière mise à jour le 9/5/2012 52 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Qualité de scène définie sur LOW et rendu du texte sur Anticrènelage pour la lisibilité Il est possible d’obtenir la même qualité de rendu en définissant le rendu du texte sur Texte bitmap (sans anticrènelage) : Qualité de scène définie sur LOW et rendu du texte sur Texte bitmap (sans anticrènelage) Les deux derniers exemples démontrent que vous pouvez obtenir du texte de qualité supérieure, quelle que soit la valeur du paramètre de qualité de scène. Cette fonction est disponible depuis Flash Player 8 et peut être utilisée sur les périphériques mobiles. Gardez à l’esprit que Flash Player 10.1 active automatiquement StageQuality.MEDIUM sur certains périphériques pour améliorer les performances. Fusion alpha Evitez si possible d’utiliser la propriété alpha. Lorsque vous utilisez la propriété alpha, évitez de créer des effets nécessitant une fusion alpha, tels que les effets de fondu. Lorsqu’un objet d’affichage utilise la fusion alpha, le moteur d’exécution doit combiner les valeurs de couleur de chaque objet d’affichage empilé et la couleur d’arrière-plan pour déterminer la couleur finale. La fusion alpha peut exiger davantage de ressources processeur que la création d’une couleur opaque, ce qui risque de diminuer les performances sur les périphériques lents. Evitez d’utiliser la propriété alpha, si possible. Dernière mise à jour le 9/5/2012 53 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Voir aussi « Mise en cache sous forme de bitmap » à la page 55 « Rendu des objets de texte » à la page 69 Cadence de l’application En règle générale, pour améliorer les performances, optez pour la plus faible cadence possible. La cadence de l’application détermine le temps disponible pour chaque cycle « code d’application et rendu » (voir « Principes fondamentaux relatifs à l’exécution du code par le moteur d’exécution » à la page 1). Plus la cadence est élevée et plus l’animation est fluide. Toutefois, en l’absence d’animation ou d’autres modifications visuelles, une cadence élevée n’a aucune raison d’être. Une cadence élevée sollicite davantage l’unité centrale et la batterie qu’une cadence inférieure. Suivez les conseils généraux suivants pour déterminer la cadence par défaut appropriée pour votre application : • Si vous utilisez la structure Flex, réglez la cadence initiale sur la valeur par défaut.. • Si l’application comprend de l’animation, une cadence de 20 images par seconde au minimum est adéquate. Il est souvent inutile de dépasser 30 images par seconde. • Si l’application ne comporte pas d’animation, une cadence de 12 images par seconde est probablement suffisante. Notez que la « plus faible cadence possible » peut varier en fonction de l’activité en cours de l’application. Pour plus d’informations, voir « Modifiez dynamiquement de la cadence de l’application ». Adoptez une cadence faible lorsque la vidéo constitue le seul contenu dynamique de l’application. Le moteur d’exécution lit le contenu vidéo chargé à sa cadence native, quelle que soit la cadence de l’application. Si l’application ne comporte pas d’animation ou d’autre contenu visuel changeant rapidement, l’adoption d’une cadence faible ne ralentit pas les performances de l’interface utilisateur. Modifiez dynamiquement la cadence de l’application. Vous définissez la cadence initiale de l’application dans les paramètres du projet ou du compilateur, mais elle n’est pas fixe. Vous pouvez la modifier à l’aide de la propriété Stage.frameRate (ou de la propriété WindowedApplication.frameRate dans Flex). Modifiez la cadence en fonction des exigences en cours de l’application. Réduisez-la lorsque l’application n’exécute pas d’animation, par exemple. Augmentez-la juste avant une transition animée. De même, si l’application s’exécute en arrière-plan (après avoir perdu le focus), vous pouvez généralement réduire encore plus la cadence. Selon toute probabilité, l’utilisateur se concentre sur une autre application ou tâche. Les conseils généraux suivants ont pour objet de vous permettre de déterminer la cadence appropriée en fonction de différentes activités : • Si vous utilisez la structure Flex, réglez la cadence initiale sur la valeur par défaut.. • Lorsqu’une animation s’exécute, réglez la cadence sur 20 images par seconde au minimum. Il est souvent inutile de dépasser 30 images par seconde. • En l’absence d’animation, une cadence de 12 images par seconde est probablement suffisante. Dernière mise à jour le 9/5/2012 54 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu • Une vidéo chargée s’exécute à sa cadence native, indépendamment de la cadence de l’application. Si la vidéo est le seul contenu en mouvement de l’application, une cadence de 12 images par seconde est probablement suffisante. • Lorsque l’application ne possède pas le focus d’entrée, une cadence de 5 images par seconde est probablement suffisante. • Lorsqu’une application AIR n’est pas visible, une cadence de 2 images par seconde au maximum est probablement suffisante. Par exemple, cette indication s’applique lors de la réduction d’une application. Elle s’applique également sur les périphériques de bureau si la propriété visible de la fenêtre native est définie sur false. Dans le cas des applications créées dans Flex, la classe spark.components. WindowedApplication prend en charge la modification dynamique de la cadence de l’application. La propriété backgroundFrameRate définit la cadence de l’application lorsque celle-ci est inactive. La valeur par défaut, 1, fait passer à 1 image par seconde la cadence d’une application créée à l’aide de la structure Spark. Vous pouvez modifier la cadence d’arrière-plan à l’aide de la propriété backgroundFrameRate. Vous pouvez définir la propriété sur une autre valeur ou la fixer à -1 pour désactiver la régulation automatique de la cadence. Pour plus d’informations sur la modification dynamique de la cadence d’une application, voir les articles suivants : • Reducing CPU usage in Adobe AIR (Article et exemple de code de Jonnie Hallman sur le pôle de développement Adobe. Disponibles en anglais uniquement) • Writing well-behaved, efficient, AIR applications (article et exemple d’application d’Arno Gourdol ; disponibles en anglais uniquement.) Grant Skinner a créé une classe de régulation de la cadence. Vous pouvez vous en servir dans vos applications pour réduire automatiquement la cadence lorsque l’application est en arrière-plan. Pour plus d’informations et pour télécharger le code source de la classe FramerateThrottler, voir l’article de Grant intitulé Idle CPU Usage in Adobe AIR and Flash Player (disponible en anglais uniquement) à l’adresse http://gskinner.com/blog/archives/2009/05/idle_cpu_usage.html. Cadence adaptative Lorsque vous compilez un fichier SWF, vous définissez la cadence (images par seconde) du clip. Dans un environnement restreint où la vitesse de l’unité centrale est faible, il arrive que la cadence baisse en cours de lecture. Pour préserver une cadence convenable côté utilisateur, le moteur d’exécution omet le rendu de certaines images. Ce processus empêche la cadence de baisser en deçà d’une valeur acceptable. Remarque : dans ce cas, le moteur d’exécution ne saute pas d’images, il n’effectue simplement pas le rendu du contenu de certaines d’entre elles. Le code est toujours exécuté et la liste d’affichage mise à jour, mais les mises à jour ne sont pas affichées. Il est impossible de spécifier un seuil d’images par seconde pour indiquer le nombre d’images dont le moteur d’exécution ne doit pas effectuer le rendu en vue d’assurer une cadence stable. Mise en cache sous forme de bitmap Appliquez la fonction de mise en cache sous forme de bitmap au contenu vectoriel complexe, lorsque cela est possible. La fonction de mise en cache sous forme de bitmap permet d’effectuer une bonne optimisation. Cette fonction met en cache un objet vectoriel, puis effectue en interne son rendu sous forme de bitmap. Elle utilise ensuite ce bitmap pour le rendu final. Si ce processus se traduit éventuellement par une nette amélioration des performances de rendu, il peut cependant être gourmand en mémoire. Utilisez la fonction de mise en cache sous forme de bitmap pour le contenu vectoriel complexe, tel que les dégradés ou le texte complexes. Dernière mise à jour le 9/5/2012 55 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu L’activation de la fonction de mise en cache sous forme de bitmap pour un objet animé contenant des graphiques vectoriels complexes (tels que du texte et des dégradés) améliore les performances. Cependant, si la fonction de mise en cache des bitmaps est activée sur un objet d’affichage tel qu’une animation dont le scénario s’exécute, vous obtenez le résultat inverse. Sur chaque image, le moteur d’exécution doit mettre à jour le bitmap mis en cache et le retracer à l’écran, ce qui sollicite fortement l’unité centrale. La fonction de mise en cache sous forme de bitmap ne présente des avantages que lorsqu’il est possible de générer une seule fois le bitmap mis en cache, puis de l’utiliser sans le mettre à jour. Si vous activez cette fonction pour un objet Sprite, il est possible de déplacer celui-ci sans que le moteur d’exécution soit obligé de régénérer le bitmap mis en cache. La modification des propriétés x et y de l’objet n’entraîne pas sa régénération. En revanche, toute tentative de rotation, mise à l’échelle ou modification de sa valeur alpha force le moteur d’exécution à régénérer le bitmap mis en cache, ce qui affecte les performances. Remarque : cette limitation ne concerne pas la propriété DisplayObject.cacheAsBitmapMatrix, que proposent AIR et l’outil Packager for iPhone. La propriété cacheAsBitmapMatrix permet de faire pivoter, mettre à l’échelle, incliner et modifier la valeur alpha d’un objet d’affichage sans déclencher une régénération de l’image bitmap. Un bitmap mis en cache peut utiliser plus de mémoire qu’une occurrence de clip courante. Un clip de 250 x 250 pixels sur la scène, par exemple, utilise environ 250 Ko une fois mis en cache, mais seulement 1 Ko lorsqu’il n’est pas mis en cache. L’exemple ci-dessous est fondé sur un objet Sprite contenant une image de pomme. La classe suivante est associée au symbole de pomme : package org.bytearray.bitmap { import flash.display.Sprite; import flash.events.Event; public class Apple extends Sprite { private var destinationX:Number; private var destinationY:Number; public function Apple () { addEventListener(Event.ADDED_TO_STAGE,activation); addEventListener(Event.REMOVED_FROM_STAGE,deactivation); } private function activation(e:Event):void { initPos(); addEventListener (Event.ENTER_FRAME,handleMovement); } private function deactivation(e:Event):void Dernière mise à jour le 9/5/2012 56 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu { removeEventListener(Event.ENTER_FRAME,handleMovement); } private function initPos():void { destinationX = Math.random()*(stage.stageWidth - (width>>1)); destinationY = Math.random()*(stage.stageHeight - (height>>1)); } private function handleMovement(e:Event):void { x -= (x - destinationX)*.5; y -= (y - destinationY)*.5; if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1) initPos(); } } } Le code utilise la classe Sprite plutôt que la classe MovieClip, car les pommes ne nécessitent pas un scénario chacune. Pour optimiser les performances, utilisez l’objet le plus léger possible. La classe est ensuite instanciée avec le code suivant : Dernière mise à jour le 9/5/2012 57 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu import org.bytearray.bitmap.Apple; stage.addEventListener(MouseEvent.CLICK,createApples); stage.addEventListener(KeyboardEvent.KEY_DOWN,cacheApples); const MAX_NUM:int = 100; var apple:Apple; var holder:Sprite = new Sprite(); addChild(holder); function createApples(e:MouseEvent):void { for (var i:int = 0; i< MAX_NUM; i++) { apple = new Apple(); holder.addChild(apple); } } function cacheApples(e:KeyboardEvent):void { if (e.keyCode == 67) { var lng:int = holder.numChildren; for (var i:int = 0; i < lng; i++) { apple = holder.getChildAt (i) as Apple; apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap); } } } Lorsque l’utilisateur effectue un clic de souris, les pommes sont créées sans être mises en cache. Lorsqu’il appuie sur la touche C (code de touche 67), les vecteurs pomme sont mis en cache sous forme de bitmaps et affichés. Cette technique améliore considérablement les performances de rendu sur le bureau et les périphériques mobiles, lorsque l’unité centrale est lente. En revanche, elle peut très rapidement se révéler très gourmande en mémoire. Dès qu’un objet est mis en cache, sa surface est capturée en tant que bitmap transparent et stockée en mémoire, comme illustré ci-dessous : Dernière mise à jour le 9/5/2012 58 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Mémoire Version du bitmap Affiché cacheAsBitmap = true Objet et son bitmap de surface stockés en mémoire Flash Player 10.1 et AIR 2.5 optimisent l’utilisation de la mémoire en appliquant la technique décrite à la section « Filtres et déchargement dynamique de bitmaps » à la page 20. Si un objet d’affichage mis en cache est masqué ou hors écran, le bitmap correspondant est libéré de la mémoire après une certaine période d’inutilisation. Remarque : si la propriété opaqueBackground de l’objet d’affichage est définie sur une couleur spécifique, le moteur d’exécution considère que l’objet est opaque. Utilisé conjointement avec la propriété cacheAsBitmap, le moteur d’exécution crée un bitmap 32 bits non transparent en mémoire. Le canal alpha est défini sur 0xFF, ce qui se traduit par une amélioration des performances, car il est inutile d’appliquer une transparence pour tracer le bitmap à l’écran. Le rendu est encore plus rapide si la fusion alpha n’est pas nécessaire. Si la profondeur d’écran actuelle est limitée à 16 bits, le bitmap en mémoire est stocké sous la forme d’une image 16 bits. L’utilisation de la propriété opaqueBackground n’active pas implicitement la mise en cache sous forme de bitmap. Pour économiser de la mémoire, utilisez la propriété cacheAsBitmap et activez-la sur chaque objet d’affichage mais pas sur le conteneur. L’activation de la mise en cache sous forme de bitmap sur le conteneur génère un bitmap final beaucoup plus gros en mémoire, soit un bitmap transparent de 211 x 279 pixels utilisant environ 229 Ko de mémoire : 211 pixels 279 pixels Activation de la mise en cache sous forme de bitmap sur le conteneur Dernière mise à jour le 9/5/2012 59 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu En outre, si vous mettez en cache le conteneur, il se peut que la totalité du bitmap soit mise à jour en mémoire, si une pomme se met à se déplacer sur une image. En revanche, l’activation de la mise en cache sous forme de bitmap sur des occurrences individuelles consiste à mettre en cache six surfaces de 7 Ko, qui consomment uniquement 42 Ko de mémoire : Activation de la mise en cache sous forme de bitmap sur les occurrences Si vous accédez à chaque occurrence de la pomme par le biais de la liste d’affichage et appelez la méthode getChildAt(), les références sont stockées dans un objet Vector pour un accès plus rapide : Dernière mise à jour le 9/5/2012 60 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu import org.bytearray.bitmap.Apple; stage.addEventListener(KeyboardEvent.KEY_DOWN, cacheApples); const MAX_NUM:int = 200; var apple:Apple; var holder:Sprite = new Sprite(); addChild(holder); var holderVector:Vector.<Apple> = new Vector.<Apple>(MAX_NUM, true); for (var i:int = 0; i< MAX_NUM; i++) { apple = new Apple(); holder.addChild(apple); holderVector[i] = apple; } function cacheApples(e:KeyboardEvent):void { if (e.keyCode == 67) { var lng:int = holderVector.length for (var i:int = 0; i < lng; i++) { apple = holderVector[i]; apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap); } } } Souvenez-vous que la mise en cache sous forme de bitmap améliore le rendu, à condition de ne pas faire pivoter, mettre à l’échelle ou modifier le contenu mis en cache sur chaque image. Cette amélioration ne se produit pas pour toute transformation autre qu’une translation sur les axes x et y. Flash Player met en effet à jour la copie du bitmap mis en cache pour chaque transformation de l’objet d’affichage. Cela peut se traduire par une forte sollicitation de l’unité centrale et de la batterie et une baisse des performances. Pour rappel, cette limitation ne concerne pas la propriété cacheAsBitmapMatrix, que proposent AIR ou l’outil Packager for iPhone Le code suivant agit sur la valeur alpha dans la méthode de mouvement, ce qui a pour effet de modifier l’opacité de la pomme à chaque image : private function handleMovement(e:Event):void { alpha = Math.random(); x -= (x - destinationX)*.5; y -= (y - destinationY)*.5; if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1) initPos(); } Dernière mise à jour le 9/5/2012 61 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu L’utilisation de la fonction de mise en cache sous forme de bitmap entraîne un ralentissement des performances. A chaque modification de la valeur alpha, le moteur d’exécution doit mettre à jour le bitmap mis en cache en mémoire. Les filtres s’attendent à ce que les bitmaps soient mis à jour à chaque déplacement de la tête de lecture d’un clip mis en cache. Par conséquent, l’application d’un filtre entraîne automatiquement la définition de la propriété cacheAsBitmap sur true. La figure suivante illustre une animation : Clip animé Il est préférable de ne pas appliquer de filtres à du contenu animé, car ils risquent d’entraîner des problèmes de performances. Dans l’exemple suivant, le concepteur ajoute un filtre d’ombre portée : Clip animé doté du filtre Ombre portée Si le scénario que contient le clip s’exécute, le bitmap doit être régénéré. Cette opération est également nécessaire si le contenu subit toute modification autre qu’une simple transformation x ou y. Le moteur d’exécution doit retracer le bitmap sur chaque image. Cette opération sollicite fortement les ressources de l’unité centrale, entraîne des performances médiocres et consomme plus de batterie. Paul Trani fournit des exemples d’utilisation de Flash Professional et d’ActionScript pour optimiser les graphiques à l’aide de bitmaps dans les vidéos de formation suivantes : • Optimizing Graphics (disponible en anglais uniquement) • Optimizing Graphics with ActionScript (disponible en anglais uniquement) Matrices de transformation des bitmaps mis en cache dans AIR Définissez la propriété cacheAsBitmapMatrix lorsque vous utilisez des bitmaps mis en cache dans les applications AIR mobiles. Dernière mise à jour le 9/5/2012 62 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Dans le profil AIR mobile, vous pouvez affecter un objet Matrix à la propriété cacheAsBitmapMatrix d’un objet d’affichage. Lorsque vous définissez cette propriété, vous pouvez appliquer une transformation bidimensionnelle à l’objet sans régénérer le bitmap mis en cache. Vous pouvez en outre modifier la propriété alpha sans régénérer le bitmap mis en cache. Il est également nécessaire de définir la propriété cacheAsBitmap sur true et de vérifier qu’aucune propriété 3D de l’objet n’est définie. La définition de la propriété cacheAsBitmapMatrix génère le bitmap mis en cache même si l’objet d’affichage est en dehors de l’écran ou masqué, ou si sa propriété visible est définie sur false. La réinitialisation de la propriété cacheAsBitmapMatrix à l’aide d’un objet Matrix contenant une transformation différente régénère également le bitmap mis en cache. La transformation de matrice que vous appliquez à la propriété cacheAsBitmapMatrix est également appliquée à l’objet d’affichage lors du rendu de ce dernier dans le cache de bitmaps. Ainsi, si la transformation contient une échelle de 2x, la taille du rendu bitmap est deux fois supérieure à celle du rendu vectoriel. Le moteur de rendu applique la transformation inverse au bitmap mis en cache afin que l’affichage final ait un aspect identique. Vous pouvez réduire la taille du bitmap mis en cache afin de limiter la consommation de mémoire, au détriment de la fidélité du rendu. Inversement, vous pouvez agrandir le bitmap pour augmenter la qualité du rendu, bien que cela risque d’accroître la consommation de mémoire. Quoi qu’il en soit, vous devez en général utiliser une matrice d’identité, c’est-à-dire une matrice qui n’applique aucune transformation, afin d’éviter toute modification de l’apparence, comme indiqué dans l’exemple suivant : displayObject.cacheAsBitMap = true; displayObject.cacheAsBitmapMatrix = new Matrix(); Une fois la propriété cacheAsBitmapMatrix définie, vous pouvez mettre à l’échelle, incliner, faire pivoter et translater l’objet sans déclencher la régénération du bitmap. Vous pouvez également modifier la valeur alpha dans une plage de valeurs comprises entre 0 et 1. Si vous modifiez la valeur alpha via la propriété transform.colorTransform avec une transformation de couleur, la valeur alpha utilisée dans l’objet de transformation doit être comprise entre 0 et 255. Modifier autrement la transformation de couleur entraîne la régénération du bitmap mis en cache. Veillez à toujours définir la propriété cacheAsBitmapMatrix chaque fois que vous définissez cacheAsBitmap sur true dans le contenu créé pour les périphériques mobiles. Vous devez toutefois prendre en compte l’inconvénient suivant. Après la rotation, la mise à l’échelle ou l’inclinaison d’un objet, le rendu final peut présenter des artefacts de crènelage ou de mise à l’échelle par rapport à un rendu vectoriel normal. Mise en cache manuelle sous forme de bitmap Utilisez la classe BitmapData pour créer un comportement personnalisé de mise en cache des bitmaps. L’exemple ci-après réutilise une version bitmap pixellisée unique d’un objet d’affichage et fait référence à un même objet BitmapData. Lors de la mise à l’échelle de chaque objet d’affichage, l’objet BitmapData original en mémoire ni n’est ni mis à jour ni redessiné. Cette technique permet d’économiser les ressources de l’unité centrale et accélère l’exécution des applications. Lors de la mise à l’échelle de l’objet d’affichage, le bitmap contenu est étiré. La classe BitmapApple mise à jour se présente comme ceci : Dernière mise à jour le 9/5/2012 63 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu package org.bytearray.bitmap { import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.Event; public class BitmapApple extends Bitmap { private var destinationX:Number; private var destinationY:Number; public function BitmapApple(buffer:BitmapData) { super(buffer); addEventListener(Event.ADDED_TO_STAGE,activation); addEventListener(Event.REMOVED_FROM_STAGE,deactivation); } private function activation(e:Event):void { initPos(); addEventListener(Event.ENTER_FRAME,handleMovement); } private function deactivation(e:Event):void { removeEventListener(Event.ENTER_FRAME,handleMovement); } private function initPos():void { destinationX = Math.random()*(stage.stageWidth - (width>>1)); destinationY = Math.random()*(stage.stageHeight - (height>>1)); } private function handleMovement(e:Event):void { alpha = Math.random(); x -= (x - destinationX)*.5; y -= (y - destinationY)*.5; if ( Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1) initPos(); } } } La valeur alpha est toujours modifiée sur chaque image. Le code suivant transmet la mémoire tampon source d’origine à chaque occurrence de BitmapApple : Dernière mise à jour le 9/5/2012 64 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu import org.bytearray.bitmap.BitmapApple; const MAX_NUM:int = 100; var holder:Sprite = new Sprite(); addChild(holder); var holderVector:Vector.<BitmapApple> = new Vector.<BitmapApple>(MAX_NUM, true); var source:AppleSource = new AppleSource(); var bounds:Object = source.getBounds(source); var mat:Matrix = new Matrix(); mat.translate(-bounds.x,-bounds.y); var buffer:BitmapData = new BitmapData(source.width+1, source.height+1, true, 0); buffer.draw(source,mat); var bitmapApple:BitmapApple; for (var i:int = 0; i< MAX_NUM; i++) { bitmapApple = new BitmapApple(buffer); holderVector[i] = bitmapApple; holder.addChild(bitmapApple); } Cette technique consomme peu de mémoire, car un seul bitmap mis en cache est utilisé en mémoire et partagé par toutes les occurrences de BitmapApple. En outre, malgré les modifications apportées aux occurrences de BitmapApple (modification de la propriété alpha, rotation ou mise à l’échelle, par exemple), le bitmap source original n’est jamais mis à jour. Cette technique empêche un ralentissement des performances. Pour obtenir au final un bitmap lisse, définissez la propriété smoothing sur true : public function BitmapApple(buffer:BitmapData) { super (buffer); smoothing = true; addEventListener(Event.ADDED_TO_STAGE, activation); addEventListener(Event.REMOVED_FROM_STAGE, deactivation); } Le réglage du paramètre de qualité de scène peut également améliorer les performances. Définissez-le sur HIGH avant la pixellisation, puis sur LOW après : Dernière mise à jour le 9/5/2012 65 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu import org.bytearray.bitmap.BitmapApple; const MAX_NUM:int = 100; var holder:Sprite = new Sprite(); addChild ( holder ); var holderVector:Vector.<BitmapApple> = new Vector.<BitmapApple>(MAX_NUM, true); var source:AppleSource = new AppleSource(); var bounds:Object = source.getBounds ( source ); var mat:Matrix = new Matrix(); mat.translate ( -bounds.x, -bounds.y ); var buffer:BitmapData = new BitmapData ( source.width+1, source.height+1, true, 0 ); stage.quality = StageQuality.HIGH; buffer.draw ( source, mat ); stage.quality = StageQuality.LOW; var bitmapApple:BitmapApple; for (var i:int = 0; i< MAX_NUM; i++ ) { bitmapApple = new BitmapApple( buffer ); holderVector[i] = bitmapApple; holder.addChild ( bitmapApple ); } La modification de la qualité de scène avant et après le dessin du vecteur dans un bitmap constitue une puissante technique de création de contenu anticrènelé à l’écran. Cette technique peut être performante quelle que soit la qualité de scène finale. Vous pouvez, par exemple, obtenir un bitmap anticrènelé à partir de texte anticrènelé, même si la qualité de scène est définie sur LOW. Cette technique n’est pas compatible avec la propriété cacheAsBitmap. Dans ce cas, la définition de la qualité de scène sur LOW entraîne la mise à jour de la qualité des vecteurs, ce qui met à jour les surfaces bitmap en mémoire et la qualité finale. Isolation de comportements Dans la mesure du possible, isolez les événements, tels que Event.ENTER_FRAME, dans un même gestionnaire. Vous pouvez optimiser encore plus le code en isolant l’événement Event.ENTER_FRAME de la classe Apple dans un gestionnaire unique. Cette technique permet d’économiser les ressources de l’unité centrale. L’exemple suivant illustre cette technique différente, selon laquelle la classe BitmapApple ne gère plus le comportement de mouvement : Dernière mise à jour le 9/5/2012 66 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu package org.bytearray.bitmap { import flash.display.Bitmap; import flash.display.BitmapData; public class BitmapApple extends Bitmap { private var destinationX:Number; private var destinationY:Number; public function BitmapApple(buffer:BitmapData) { super (buffer); smoothing = true; } } Le code suivant instancie les pommes et gère leur mouvement dans un même gestionnaire : import org.bytearray.bitmap.BitmapApple; const MAX_NUM:int = 100; var holder:Sprite = new Sprite(); addChild(holder); var holderVector:Vector.<BitmapApple> = new Vector.<BitmapApple>(MAX_NUM, true); var source:AppleSource = new AppleSource(); var bounds:Object = source.getBounds(source); var mat:Matrix = new Matrix(); mat.translate(-bounds.x,-bounds.y); stage.quality = StageQuality.BEST; var buffer:BitmapData = new BitmapData(source.width+1,source.height+1, true,0); buffer.draw(source,mat); stage.quality = StageQuality.LOW; var bitmapApple:BitmapApple; for (var i:int = 0; i< MAX_NUM; i++) { bitmapApple = new BitmapApple(buffer); bitmapApple.destinationX = Math.random()*stage.stageWidth; bitmapApple.destinationY = Math.random()*stage.stageHeight; holderVector[i] = bitmapApple; holder.addChild(bitmapApple); } stage.addEventListener(Event.ENTER_FRAME,onFrame); Dernière mise à jour le 9/5/2012 67 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu var lng:int = holderVector.length function onFrame(e:Event):void { for (var i:int = 0; i < lng; i++) { bitmapApple = holderVector[i]; bitmapApple.alpha = Math.random(); bitmapApple.x -= (bitmapApple.x - bitmapApple.destinationX) *.5; bitmapApple.y -= (bitmapApple.y - bitmapApple.destinationY) *.5; if (Math.abs(bitmapApple.x - bitmapApple.destinationX ) < 1 && Math.abs(bitmapApple.y - bitmapApple.destinationY ) < 1) { bitmapApple.destinationX = Math.random()*stage.stageWidth; bitmapApple.destinationY = Math.random()*stage.stageHeight; } } } Un seul événement Event.ENTER_FRAME gère donc le mouvement, à la place de 200 gestionnaires déplaçant chaque pomme. Il est facile de mettre toute l’animation en pause, ce qui peut être pratique dans un jeu. Un jeu simple, par exemple, peut utiliser le gestionnaire suivant : stage.addEventListener(Event.ENTER_FRAME, updateGame); function updateGame (e:Event):void { gameEngine.update(); } L’étape suivante consiste à faire interagir les pommes avec la souris ou le clavier, ce qui nécessite de modifier la classe BitmapApple. package org.bytearray.bitmap { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; public class BitmapApple extends Sprite { public var destinationX:Number; public var destinationY:Number; private var container:Sprite; private var containerBitmap:Bitmap; public function BitmapApple(buffer:BitmapData) { container = new Sprite(); containerBitmap = new Bitmap(buffer); containerBitmap.smoothing = true; container.addChild(containerBitmap); addChild(container); } } Dernière mise à jour le 9/5/2012 68 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Il en résulte des occurrences de BitmapApple interactives, à l’instar des objets Sprite classiques. Les occurrences sont toutefois liées à un bitmap unique, qui n’est pas rééchantillonné lors de la transformation des objets d’affichage. Rendu des objets de texte Conjuguez la fonction de mise en cache sous forme de bitmap et la propriété opaqueBackground pour améliorer les performances de rendu du texte. Flash Text Engine propose d’excellentes optimisations. Cependant, de nombreuses classes sont nécessaires pour afficher une seule ligne de texte. Pour créer un champ de texte modifiable à l’aide de la classe TextLine exige donc beaucoup de mémoire et de nombreuses lignes de code ActionScript. Cette classe est mieux adaptée au texte statique non modifiable. Elle assure dans ce cas un rendu plus rapide et consomme moins de mémoire. La fonction de mise en cache sous forme de bitmap permet de mettre en cache du contenu vectoriel sous forme de bitmaps pour améliorer les performances de rendu. Elle convient au contenu vectoriel complexe et au texte dont le rendu nécessite un traitement préliminaire. L’exemple suivant montre comment conjuguer la fonction de mise en cache sous forme de bitmap et la propriété opaqueBackground pour améliorer les performances de rendu. La figure ci-dessous illustre un écran de bienvenue standard présenté à l’utilisateur lors du chargement de ressources : Ecran de bienvenue La figure suivante illustre l’accélération appliquée à l’objet TextField par programmation. Le texte est accéléré lentement du haut de la scène vers le centre de celle-ci : Dernière mise à jour le 9/5/2012 69 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Accélération de texte Le code suivant crée l’accélération. La variable preloader stocke l’objet cible actif pour réduire les recherches de propriétés, qui peuvent avoir une incidence sur les performances : wait_mc.addEventListener( Event.ENTER_FRAME, movePosition ); var destX:Number=stage.stageWidth/2; var destY:Number=stage.stageHeight/2; var preloader:DisplayObject; function movePosition( e:Event ):void { preloader = e.currentTarget as DisplayObject; preloader.x -= ( preloader.x - destX ) * .1; preloader.y -= ( preloader.y - destY ) * .1; if (Math.abs(preloader.y-destY)<1) preloader.removeEventListener( Event.ENTER_FRAME, movePosition ); } Dernière mise à jour le 9/5/2012 70 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Dans cet exemple, vous pourriez placer la fonction Math.abs() en ligne pour réduire le nombre des appels de fonction et améliorer encore plus les performances. Il est recommandé de définir les propriétés destX et destY sur le type int pour obtenir des valeurs à virgule fixe. L’utilisation du type int assure un accrochage aux pixels parfait sans avoir à arrondir manuellement les valeurs par le biais de méthodes lentes telles queMath.ceil() ou Math.round(). Ce code n’arrondit pas les coordonnées à des valeurs entières car, en conséquence d’un arrondissement constant, le déplacement de l’objet n’est pas fluide. Ses mouvements peuvent être saccadés, car les coordonnées sont accrochées aux entiers arrondis les plus proches sur chaque image. Cette technique peut cependant être utile pour définir la position finale d’un objet d’affichage. N’utilisez pas le code suivant : // Do not use this code var destX:Number = Math.round ( stage.stageWidth / 2 ); var destY:Number = Math.round ( stage.stageHeight / 2); Le code suivant est beaucoup plus rapide : var destX:int = stage.stageWidth / 2; var destY:int = stage.stageHeight / 2; Il est possible d’optimiser encore plus le code précédent en utilisant des opérateurs de décalage au niveau du bit pour diviser les valeurs : var destX:int = stage.stageWidth >> 1; var destY:int = stage.stageHeight >> 1; Grâce à la fonction de mise en cache sous forme de bitmap, le moteur d’exécution peut utiliser des bitmaps dynamiques, ce qui facilite le rendu des objets. Dans le présent exemple, le clip contenant l’objet TextField est en cache : wait_mc.cacheAsBitmap = true; Pour améliorer les performances, vous pouvez aussi supprimer la transparence alpha. Celle-ci surcharge le moteur d’exécution lors de la création d’images bitmap transparentes, comme dans le code précédent. Pour contourner cette difficulté, vous pouvez utiliser la propriété opaqueBackground et spécifier une couleur comme arrière-plan. Lorsque vous utilisez la propriété opaqueBackground, la surface bitmap créée en mémoire occupe encore 32 bits. Le décalage alpha est toutefois défini sur 255 et aucune transparence n’est appliquée. La propriété opaqueBackground ne permet donc pas de réduire la consommation de mémoire mais, lorsqu’elle est alliée à la fonction de mise en cache sous forme de bitmap, elle améliore les performances de rendu. Le code suivant conjugue toutes les possibilités d’optimisation : Dernière mise à jour le 9/5/2012 71 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu wait_mc.addEventListener( Event.ENTER_FRAME, movePosition ); wait_mc.cacheAsBitmap = true; // Set the background to the color of the scene background wait_mc.opaqueBackground = 0x8AD6FD; var destX:int = stage.stageWidth >> 1; var destY:int = stage.stageHeight >> 1; var preloader:DisplayObject; function movePosition ( e:Event ):void { preloader = e.currentTarget as DisplayObject; preloader.x -= ( preloader.x - destX ) * .1; preloader.y -= ( preloader.y - destY ) * .1; if ( Math.abs ( preloader.y - destY ) < 1 ) e.currentTarget.removeEventListener ( Event.ENTER_FRAME, movePosition ); } L’animation est maintenant optimisée. La mise en cache sous forme de bitmap l’est également en conséquence de la suppression de la transparence. Sur les périphériques mobiles, vous pouvez éventuellement faire passer la qualité de scène de LOW à HIGH (et inversement) au cours des différents états de l’animation tout en utilisant la fonction de mise en cache sous forme de bitmap : wait_mc.addEventListener( Event.ENTER_FRAME, movePosition ); wait_mc.cacheAsBitmap = true; wait_mc.opaqueBackground = 0x8AD6FD; // Switch to low quality stage.quality = StageQuality.LOW; var destX:int = stage.stageWidth>>1; var destY:int = stage.stageHeight>>1; var preloader:DisplayObject; function movePosition( e:Event ):void { preloader = e.currentTarget as DisplayObject; preloader.x -= ( preloader.x - destX ) * .1; preloader.y -= ( preloader.y - destY ) * .1; if (Math.abs(e.currentTarget.y-destY)<1) { // Switch back to high quality stage.quality = StageQuality.HIGH; preloader.removeEventListener( Event.ENTER_FRAME, movePosition ); } } Dans ce cas, le moteur d’exécution doit cependant régénérer la surface bitmap de l’objet TextField afin qu’il corresponde au paramètre de qualité de scène actif. Il est donc préférable de ne pas modifier ce paramètre lorsque vous utilisez la fonction de mise en cache sous forme de bitmap. Cet exemple se prête également à une technique de mise en cache sous forme de bitmap manuelle. Pour simuler la propriété opaqueBackground, il est possible de dessiner le clip sur un objet BitmapData non transparent ; le moteur d’exécution n’est alors pas obligé de régénérer la surface bitmap. Dernière mise à jour le 9/5/2012 72 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Cette technique est adaptée au contenu qui ne change pas à terme. En revanche, s’il est possible de modifier le contenu du champ de texte, faites si possible appel à une autre stratégie. Imaginons qu’un champ de texte soit constamment mis à jour pour indiquer le pourcentage de l’application qui est chargé. Si le champ de texte, ou l’objet d’affichage le contenant, a été mis en cache sous forme de bitmap, sa surface doit être générée chaque fois que le contenu change. Il est impossible d’utiliser la mise en cache sous forme de bitmap manuelle dans ce cas, car le contenu de l’objet d’affichage change sans cesse. Vous seriez alors forcé d’appeler manuellement la méthode BitmapData.draw() pour mettre à jour le bitmap mis en cache. Pour rappel, depuis Flash Player 8 (et AIR 1.0), quelle que soit la valeur de la qualité de scène, un champ de texte dont le rendu est défini sur Anticrènelage pour la lisibilité reste parfaitement anticrènelé. Cette technique est moins gourmande en mémoire mais elle sollicite plus l’unité centrale et le rendu est un peu moins rapide qu’avec la fonction de mise en cache sous forme de bitmap. Le code suivant repose sur cette technique : wait_mc.addEventListener( Event.ENTER_FRAME, movePosition ); // Switch to low quality stage.quality = StageQuality.LOW; var destX:int = stage.stageWidth >> 1; var destY:int = stage.stageHeight >> 1; var preloader:DisplayObject; function movePosition ( e:Event ):void { preloader = e.currentTarget as DisplayObject; preloader.x -= ( preloader.x - destX ) * .1; preloader.y -= ( preloader.y - destY ) * .1; if ( Math.abs ( preloader.y - destY ) < 1 ) { // Switch back to high quality stage.quality = StageQuality.HIGH; preloader.removeEventListener ( Event.ENTER_FRAME, movePosition ); } } Il est déconseillé d’utiliser l’option Anticrènelage pour la lisibilité pour le texte en mouvement. Lorsqu’elle est conjuguée à une mise à l’échelle du texte, le texte tente de rester aligné, ce qui produit un effet de décalage. Toutefois, si le contenu de l’objet d’affichage change constamment et qu’il est nécessaire de mettre le texte à l’échelle, vous pouvez améliorer les performances sur les applications mobiles en définissant la qualité sur LOW. Au terme du mouvement, redéfinissez la qualité sur HIGH. Dernière mise à jour le 9/5/2012 73 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Processeur graphique Rendu sur GPU dans les applications Flash Player Une nouvelle fonction importante de Flash Player 10.1 lui permet d’utiliser le processeur graphique pour effectuer le rendu du contenu graphique sur les périphériques mobiles. Auparavant, l’unité centrale effectuait seule le rendu des graphiques. L’utilisation du processeur graphique optimise le rendu des filtres, des bitmaps, de la vidéo et du texte. Gardez à l’esprit que le rendu par le processeur graphique n’est pas toujours aussi précis que le rendu logiciel. Lorsque vous utilisez le rendu matériel, la définition du contenu à l’écran est moins nette. En outre, une limitation de Flash Player 10.1 peut empêcher le rendu à l’écran des effets Pixel Bender. Lors de l’utilisation de l’accélération matérielle, ces effets sont parfois rendus sous la forme d’un carré noir. Flash Player 10 possédait une fonction d’accélération du processeur graphique. Il n’utilisait néanmoins pas le processeur pour calculer les graphiques, mais simplement pour les envoyer à l’écran. Flash Player 10.1 utilise le processeur graphique pour le calcul des graphiques, d’où une amélioration sensible de la vitesse de rendu. La charge de travail de l’unité centrale est également réduite, ce qui est utile sur les périphériques aux ressources limitées, tels que les périphériques mobiles. Le mode processeur graphique est automatiquement activé lors de l’exécution de contenu sur les périphériques mobiles, en vue d’assurer les meilleures performances possibles. Bien qu’il ne soit plus nécessaire de définir wmode sur gpu pour que le processeur graphique effectue le rendu, l’accélération matérielle est désactivée si vous réglez wmode sur opaque ou transparent. Remarque : la version de bureau de Flash Player utilise toujours l’unité centrale pour le rendu logiciel, parce qu’il existe une grande variété de pilotes pour le bureau et qu’ils peuvent accentuer les différences de rendu. Le rendu sur le bureau et sur certains périphériques mobiles peut également être différent. Rendu sur GPU dans les applications AIR mobiles Pour activer l’accélération matérielle des images dans une application AIR, insérez <renderMode>gpu</renderMode> dans le descripteur d’application. Il est impossible de modifier les modes de rendu à l’exécution. Sur les ordinateurs de bureau, le paramètre renderMode est ignoré ; l’accélération des images via le processeur graphique n’est actuellement pas prise en charge. Limitations du mode de rendu sur GPU Il existe certaines limitations lors de l’utilisation du mode de rendu sur GPU dans AIR 2.5 : • Si le processeur graphique ne parvient pas à effectuer le rendu d’un objet, ce dernier ne s’affiche pas. Il n’existe aucune autre alternative au rendu sur GPU. • Les modes de fusion suivants ne sont pas pris en charge : Calque, Alpha, Effacement, Superposition, Lumière crue, Eclaircir et Obscurcir. • Les filtres ne sont pas pris en charge. • PixelBender n’est pas pris en charge. • De nombreux processeurs graphiques disposent d’une taille de texture maximale de 1024 x 1024. Dans ActionScript, cela se traduit par la taille de rendu final maximale d’un objet d’affichage après toutes les transformations. • Adobe ne recommande pas l’utilisation du mode de rendu sur GPU dans les applications AIR compatibles avec la vidéo. Dernière mise à jour le 9/5/2012 74 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu • En mode de rendu sur GPU, les champs de texte ne sont pas toujours déplacés vers un emplacement visible lors de l’ouverture du clavier virtuel. Pour vous assurer que le champ de texte est visible lorsque l’utilisateur saisit du texte, effectuez l’une des opérations suivantes. Placez le champ de texte dans la moitié supérieure de l’écran ou déplacezle vers la moitié supérieure de l’écran lorsqu’il reçoit le focus. • Le mode de rendu sur GPU est désactivé sur certains périphériques sur lesquels il ne fonctionne pas correctement. Voir les notes de version du développeur AIR pour obtenir les dernières informations. Normes de bonnes pratiques en matière de rendu sur GPU Les conseils suivants permettent d’accélérer le rendu sur GPU : • Limitez le nombre d’éléments visibles sur la scène. Le rendu de chaque élément et son impact sur les éléments qui l’entourent prennent un certain temps. Si vous ne souhaitez plus afficher un objet d’affichage, définissez sa propriété visible sur false. Ne vous contentez pas de le déplacer hors de la scène, placez-le derrière un autre objet afin de le masquer ou définissez sa propriété alpha sur 0. Si vous n’avez plus besoin de l’objet d’affichage, supprimez-le de la scène à l’aide de la propriété removeChild(). • Réutilisez les objets au lieu de les créer et les détruire. • Créez des bitmaps dont la taille est inférieure à 2n x 2m bits, mais proche de cette valeur. Si les dimensions ne sont pas nécessairement exprimées sous la forme 2 à la puissance n, elles ne doivent ni s’en éloigner, ni dépasser cette valeur. Ainsi, le rendu d’une image de 31 x 15 pixels est plus rapide que celui d’une image de 33 x 17 pixels. (32 et 16 sont des puissances de 2, et 31 et 15 sont des valeurs légèrement inférieures.) • Dans la mesure du possible, définissez le paramètre repeat sur false lorsque vous appelez la méthode Graphic.beginBitmapFill(). • Ne dessinez pas inutilement. Appliquez la couleur d’arrière-plan en tant qu’arrière-plan. Ne superposez pas les formes de grande taille. A chaque pixel à dessiner correspond une charge de traitement. • Evitez les formes à pointes longues et fines, les bords qui se recoupent ou un grand nombre de détails le long des bords. Le rendu de ces formes est en effet plus long que celui des objets d’affichage aux bords lisses. • Limitez la taille des objets d’affichage. • Activez cacheAsBitMap et cacheAsBitmapMatrix pour les objets d’affichage dont les images ne sont pas mises à jour fréquemment. • Evitez de créer des graphiques par le biais de l’API de dessin d’ActionScript (classe Graphics). Dans la mesure du possible, créez ces objets de façon statique au moment de la création. • Mettez à l’échelle les actifs bitmaps à la taille finale avant de les importer. Mode de rendu sur GPU dans les applications AIR 2.0.3 mobiles Le rendu sur GPU est soumis à davantage de restrictions dans les applications AIR mobiles créées avec l’outil Packager for iPhone. Le rendu sur GPU n’est efficace que sur les bitmaps, les formes pleines et les objets d’affichage dont la propriété cacheAsBitmap est définie. Le processeur peut par ailleurs effectuer le rendu des objets qui pivotent ou sont mis à l’échelle, et dont les propriétés cacheAsBitmap et cacheAsBitmapMatrix sont activées. Le processeur est utilisé en tandem pour d’autres objets d’affichage, ce qui entraîne généralement des performances de rendu médiocres. Conseils pour l’optimisation des performances du rendu sur GPU Bien que le rendu sur GPU améliore nettement les performances du contenu SWF, la conception du contenu joue une rôle primordial. N’oubliez pas que les paramètres ayant prouvé leur efficacité dans le rendu logiciel ne sont parfois pas adaptés au rendu sur GPU. Les conseils suivants peuvent vous aider à optimiser les performances du rendu sur GPU sans entraver celles du rendu logiciel. Dernière mise à jour le 9/5/2012 75 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Remarque : Les périphériques mobiles qui prennent en charge le rendu matériel accèdent souvent au contenu SWF via Internet. Il est donc fortement recommandé de tenir compte des conseils suivants lors de la création de contenu SWF afin d’obtenir les meilleurs résultats possibles sur tous les écrans. • Evitez d’utiliser les modes wmode=transparent ou wmode=opaque dans les paramètres embed HTML, car ils peuvent réduire les performances. Ils peuvent également avoir une incidence négative sur la synchronisation audio/vidéo lors du rendu logiciel et matériel. Par ailleurs, de nombreuses plates-formes ne prennent pas en charge le rendu sur GPU lorsque ces modes sont activés, ce qui réduit les performances de façon significative. • Utilisez uniquement le mode normal et le mode de fusion alpha. Evitez d’utiliser d’autres modes de fusion, notamment le mode de fusion des calques. Il est impossible de reproduire fidèlement tous les modes de fusion lors du rendu sur GPU. • Lorsqu’un GPU effectue le rendu d’images vectorielles, il rompt ces dernières en maillages composés de petits triangles avant de les dessiner. Ce processus est appelé « réduction en mosaïque ». La réduction en mosaïque implique un faible coût de performances, qui s’accroît au fur et à mesure qu’augmente la complexité de la forme. Pour minimiser l’impact sur les performances, évitez les formes morphes, que le rendu sur GPU réduit en mosaïque sur chaque image. • Evitez les courbes qui se croisent, les régions fines courbées (comme une fine lune croissante), ainsi que les détails compliqués le long des bords d’une forme. Ces formes sont trop complexes pour que le GPU les réduise en maillages triangulaires. Pour comprendre pourquoi, prenons deux vecteurs : un carré de 500 × 500 et une lune croissante de 100 × 10. Un GPU peut facilement effectuer le rendu du carré, car il est composé uniquement de deux triangles. En revanche, de nombreux triangles sont nécessaires pour décrire la courbe de la lune croissante. Le rendu de la forme est donc plus compliqué même s’il implique moins de pixels. • Evitez les changements d’échelle trop importants, car ils peuvent également obliger le GPU à réduire une nouvelle fois les images en mosaïque. • Dans la mesure du possible, évitez de dessiner à l’excès, c’est-à-dire de créer des couches de plusieurs éléments graphiques de façon à les masquer les uns des autres. Grâce au rendu logiciel, chaque pixel est dessiné une seule fois. Ainsi, pour le rendu logiciel, l’application n’affecte pas les performances, quel que soit le nombre d’éléments graphiques qui se chevauchent à un emplacement de pixel. A l’inverse, le rendu logiciel dessine chaque pixel pour chaque élément, que d’autres éléments masquent ou pas cette région. Si deux rectangles se chevauchent, le rendu matériel dessine la région chevauchée deux fois alors que le rendu logiciel ne la dessine qu’une seule fois. Ainsi, l’excès de dessin sur le bureau (qui utilise le rendu logiciel) n’a normalement aucun impact sur les performances. De nombreuses formes se chevauchant peuvent néanmoins avoir une incidence négative sur les périphériques qui utilisent le rendu sur GPU. Il est donc conseillé de supprimer les objets de la liste d’affichage au lieu de les masquer. • Evitez d’utiliser un grand rectangle rempli comme arrière-plan. Définissez plutôt la couleur d’arrière-plan de la scène. • Dans la mesure du possible, évitez d’utiliser le mode de remplissage bitmap par défaut de la répétition de bitmaps. Utilisez plutôt le mode de verrouillage de bitmaps pour obtenir de meilleures performances. Opérations asynchrones Préférez les versions asynchrones aux versions synchrones des opérations, si possible. Dernière mise à jour le 9/5/2012 76 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Les opérations synchrones s’exécutent immédiatement et le code attend la fin de leur exécution avant de continuer. Elles s’exécutent donc dans la phase code d’application de la boucle d’image. Si une opération synchrone est longue, elle étire la taille de la boucle d’image et l’affichage risque de se bloquer ou d’être saccadé. Une opération asynchrone, en revanche, ne s’exécute pas nécessairement immédiatement. Votre code et d’autres lignes de code d’application du thread d’exécution actif continuent de s’exécuter. Le moteur d’exécution exécute l’opération dès que possible tout en essayant de parer aux problèmes de rendu. Dans certains cas, l’exécution se déroule en arrière-plan et ne fait pas partie de la boucle d’image. Au terme de l’opération, le moteur d’exécution distribue un événement que le code peut écouter en vue d’exécuter d’autres tâches. Les opérations asynchrones sont planifiées et divisées pour parer aux problèmes de rendu. Elles permettent donc d’accroître la réactivité de l’application (voir « Perception et réalité en matière de performances » à la page 3 pour plus d’informations). Les opérations asynchrones nécessitent néanmoins plus de temps système. Dans la pratique, leur exécution peut donc durer plus longtemps, surtout lorsqu’elles sont très courtes. Dans le moteur d’exécution, de nombreuses opérations sont synchrones ou asynchrones de par leur nature, et il est impossible de choisir leur mode d’exécution. Adobe Air, en revanche, propose trois types d’opérations que vous pouvez exécuter de manière synchrone ou asynchrone, au choix : • Opérations des classes File et FileStream Il est possible d’exécuter un grand nombre des opérations de la classe File de manière synchrone ou asynchrone. Ainsi, il existe une version asynchrone des méthodes de copie ou de suppression de fichier ou de répertoire et d’affichage du contenu d’un répertoire. Le nom de la version asynchrone est identifié par le suffixe « Async ». Pour supprimer un fichier de manière asynchrone, par exemple, appelez la méthode File.deleteFileAsync() et non la méthode File.deleteFile(). Lorsque vous accédez à un fichier en lecture ou en écriture à l’aide d’un objet FileStream, c’est la façon dont vous ouvrez celui-ci qui détermine si les opérations de lecture ou d’écriture sont synchrones ou asynchrones. Utilisez la méthode FileStream.openAsync() pour les opérations asynchrones. L’écriture des données est exécutée en mode asynchrone. La lecture des données est effectuée par blocs, elles sont donc disponibles progressivement. En revanche, le mode synchrone de l’objet FileStream lit intégralement le fichier avant de poursuivre l’exécution du code. • Opérations de base de données SQL locale Lorsque vous utilisez une base de données SQL locale, toutes les opérations exécutées via un objet SQLConnection sont en mode synchrone ou asynchrone. Pour spécifier leur exécution en mode asynchrone, ouvrez la connexion à la base de données à l’aide de la méthode SQLConnection.openAsync() plutôt que de la méthode SQLConnection.open(). Les opérations de base de données asynchrones s’exécutent en arrière-plan. Le moteur de base de données ne s’exécute pas dans la boucle d’image du moteur d’exécution, si bien que les opérations de base de données sont peu susceptibles de donner lieu à des problèmes de rendu. Vous trouverez d’autres stratégies d’amélioration des performances avec la base de données SQL locale à la section « Performances de la base de données SQL » à la page 91. • Shaders autonomes de Pixel Bender La classe ShaderJob permet d’exécuter une image ou un ensemble de données via un shader de Pixel Bender et d’accéder aux données résultantes brutes. Lorsque vous appelez la méthode ShaderJob.start(), le shader s’exécute de manière asynchrone par défaut. L’exécution a lieu en arrière-plan, pas dans la boucle d’image du moteur d’exécution. Pour imposer l’exécution en mode synchrone de l’objet ShaderJob (ce qui n’est pas recommandé), transmettez la valeur true au premier paramètre de la méthode start(). Dernière mise à jour le 9/5/2012 77 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Outre ces mécanismes intégrés permettant d’utiliser le mode asynchrone, vous pouvez aussi structurer votre code de sorte à l’exécuter en mode asynchrone et non synchrone. Si vous programmez une tâche qui risque d’être longue, vous pouvez structurer le code de sorte à exécuter la tâche par blocs. Cette division permet au moteur d’exécution d’effectuer ses opérations de rendu entre les blocs d’exécution du code, réduisant ainsi l’éventualité de problèmes de rendu. Diverses techniques de division du code sont recensées ci-après. Elles ont pour but principal de vous permettre de programmer du code n’exécutant pas toutes les tâches en une seule fois. Vous assurez le suivi du code et de ses arrêts. Grâce à un mécanisme tel qu’un objet Timer, vous vérifiez à plusieurs reprises s’il reste des tâches et vous les exécutez par blocs jusqu’à ce qu’elles soient toutes terminées. Pour structurer du code afin qu’il divise les tâches, il est nécessaire de respecter certains modèles établis. Les articles et bibliothèques de code ci-dessous décrivent ces modèles et contiennent du code qui vous permettra de les implémenter dans vos applications : • Asynchronous ActionScript Execution (Article détaillé de Trevor McCauley, s’accompagnant d’exemples d’implémentation. Disponible en anglais uniquement.) • Parsing & Rendering Lots of Data in Flash Player (Article détaillé de Jesse Warden, s’accompagnant d’exemples d’implémentation de deux techniques, « patron de conception Builder » et « threads verts ». Disponible en anglais uniquement.) • Green Threads (Article de Drew Cummins décrivant la technique « threads verts » et s’accompagnant d’un exemple de code source. Disponible en anglais uniquement) • greenthreads (Bibliothèque de code de type open source créée par Charlie Hubbard pour l’implémentation des « threads verts » dans ActionScript. Disponible en anglais uniquement. Voir greenthreads Quick Start pour plus d’informations. Disponible en anglais uniquement.) • Threads dans ActionScript 3, à l’adresse http://www.adobe.com/go/learn_fp_as3_threads_fr (Article d’Alex Harui, comprenant un exemple d’implémentation de la technique de « pseudo threading ». Disponible en anglais uniquement.) Fenêtres transparentes Dans les applications AIR de bureau, envisagez d’utiliser une fenêtre d’application rectangulaire opaque et non transparente. Pour créer une fenêtre d’application initiale opaque d’une application AIR de bureau, définissez la valeur suivante dans le fichier descripteur XML de l’application : <initialWindow> <transparent>false</transparent> </initialWindow> Si la fenêtre est créée par le code d’application, créez un objet NativeWindowInitOptions et réglez sa propriété transparent sur false (paramétrage par défaut). Transmettez-le au constructeur NativeWindow pendant la création de l’objet NativeWindow : // NativeWindow: flash.display.NativeWindow class var initOptions:NativeWindowInitOptions = new NativeWindowInitOptions(); initOptions.transparent = false; var win:NativeWindow = new NativeWindow(initOptions); Si la fenêtre est un composant Window Flex, veillez à ce que sa propriété de transparence soit définie sur false (paramétrage par défaut) avant d’appeler la méthode open() de l’objet Window. Dernière mise à jour le 9/5/2012 78 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu // Flex window component: spark.components.Window class var win:Window = new Window(); win.transparent = false; win.open(); Une partie du bureau de l’utilisateur ou d’autres fenêtres d’application sont potentiellement visibles au travers d’une fenêtre transparente. Le moteur d’exécution consomme donc plus de ressources pour effectuer son rendu. Le rendu d’une fenêtre rectangulaire opaque, qu’elle utilise un chrome système ou personnalisé, est plus simple. Optez pour une fenêtre transparente uniquement lorsqu’il est important que l’affichage ne soit pas rectangulaire ou pour afficher le contenu en arrière-plan au travers de la fenêtre. Lissage des formes vectorielles Lissez les formes pour optimiser les performances de rendu. Contrairement aux bitmaps, le rendu du contenu vectoriel requiert de nombreux calculs, notamment pour les gradients et les tracés complexes qui contiennent de nombreux points de contrôle. En qualité de concepteur ou développeur, veillez à ce que les formes soient suffisamment optimisées. La figure suivante illustre les tracés non simplifiés comprenant de nombreux points de contrôle : Tracés non optimisés Utilisez l’outil de lissage de Flash Professional pour supprimer les points de contrôle en trop. Un outil équivalent est disponible dans Adobe® Illustrator® ; il est possible d’afficher le nombre total de points et de tracés dans le panneau Informations sur le document. Le lissage supprime les points de contrôle en trop, ce qui réduit la taille finale du fichier SWF et améliore les performances de rendu. La figure suivante illustre les mêmes tracés après le lissage : Dernière mise à jour le 9/5/2012 79 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de rendu Tracés optimisés Tant que vous ne simplifiez pas à l’extrême les tracés, cette optimisation ne change rien visuellement. Il est toutefois possible d’améliorer de façon significative la cadence moyenne de votre application finale en simplifiant les tracés complexes. Dernière mise à jour le 9/5/2012 80 81 Chapitre 6 : Optimisation de l’interaction avec le réseau Amélioration en vue de l’interaction avec le réseau Flash Player 10.1 et AIR 2.5 offrent une série de nouvelles fonctions destinées à l’optimisation réseau sur toutes les plates-formes, notamment la mise en mémoire tampon circulaire et la recherche dynamique. Mise en mémoire tampon circulaire Lors du chargement de contenu multimédia sur des périphériques mobiles, il peut se produire des problèmes qui ne surviennent pratiquement jamais sur un ordinateur de bureau. Il est plus probable, par exemple, que la mémoire ou l’espace disque soit saturé. Lors du chargement d’une vidéo, les versions de bureau de Flash Player 10.1 et d’AIR 2.5 téléchargent et mettent en cache la totalité du fichier FLV (ou fichier MP4) sur le disque dur. Le moteur d’exécution lit ensuite la vidéo à partir du fichier mis en cache. Il est donc rare que l’espace disque vienne à manquer. Si cela se produit, le moteur d’exécution de bureau arrête la lecture de la vidéo. Sur un périphérique mobile, il arrive beaucoup plus fréquemment que l’espace disque soit saturé. Si tel est le cas, le moteur d’exécution n’arrête pas la lecture comme c’est le cas sur la version de bureau. Il reprend l’écriture du fichier en cache depuis le début de celui-ci. L’utilisateur peut continuer à visionner la vidéo. Il lui est impossible d’effectuer une recherche dans la portion de la vidéo qui a été récrite, sauf au début du fichier. La mise en mémoire tampon circulaire ne démarre pas par défaut. Il est possible de la démarrer au cours de la lecture et au début de celle-ci si la taille de la vidéo est supérieure à l’espace disque ou la mémoire RAM disponible. Le moteur d’exécution nécessite au moins 4 Mo de mémoire RAM ou 20 Mo d’espace disque pour pouvoir utiliser la mise en mémoire tampon circulaire. Remarque : si l’espace disque est suffisant sur le périphérique, la version mobile du moteur d’exécution se comporte comme la version de bureau. Une mémoire tampon en RAM est utilisée comme méthode de secours si le périphérique ne possède pas de disque ou si le disque est saturé. Vous pouvez limiter la taille du fichier en cache et de la mémoire tampon en RAM lors de la compilation. La structure de certains fichiers MP4 est telle qu’il est nécessaire de télécharger intégralement le fichier avant que la lecture puisse commencer. Le moteur d’exécution détecte ces fichiers et bloque leur téléchargement si l’espace disque est insuffisant. Dans ce cas, il est donc impossible de les lire. Il peut être préférable de ne pas demander le téléchargement de ces fichiers. En tant que développeur, pensez qu’il est uniquement possible d’effectuer une recherche au sein du flux en cache. Il arrive que NetStream.seek() échoue si le décalage est hors limites, auquel cas un événement NetStream.Seek.InvalidTime est distribué. Recherche dynamique Remarque : la fonction de recherche dynamique requiert Adobe® Flash® Media Server 3.5.3. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau Flash Player 10.1 et AIR 2.5 proposent un nouveau comportement, la recherche dynamique, qui améliore l’expérience de l’utilisateur lors de la lecture de vidéos en flux continu. Si l’utilisateur recherche une destination au sein de la mémoire tampon, le moteur d’exécution réutilise celle-ci pour assurer une recherche instantanée, ce qui n’était pas le cas dans les versions précédentes du moteur d’exécution. Par exemple, si un utilisateur lisait une vidéo à partir d’un serveur de diffusion en continu, que le délai de mise en tampon (NetStream.bufferTime) était défini à 20 secondes et que l’utilisateur effectuait une recherche en avant de 10 secondes, le moteur d’exécution supprimait toutes les données en mémoire tampon plutôt que de réutiliser les 10 secondes déjà chargées. Le moteur d’exécution devait alors demander de nouvelles données au serveur beaucoup plus fréquemment, d’où des performances de lecture médiocres sur les connexions lentes. La figure ci-dessous illustre le comportement de la mémoire tampon dans la version précédente du moteur d’exécution. La propriété bufferTime détermine le nombre de secondes à précharger à l’avance pour qu’il soit possible d’utiliser la mémoire tampon sans arrêter la vidéo en cas de perte de la connexion : Mémoire tampon Tête de lecture Comportement de la mémoire tampon avant la fonction de recherche dynamique Grâce à la fonction de recherche dynamique, le moteur d’exécution utilise désormais la mémoire tampon lors d’une recherche en avant et en arrière au sein de la vidéo. Ce nouveau comportement est illustré ci-dessous : Mémoire tampon Tête de lecture Recherche en avant avec la fonction recherche dynamique Mémoire tampon Tête de lecture Recherche en arrière avec la fonction recherche dynamique La recherche dynamique réutilise la mémoire tampon pendant une recherche en avant ou en arrière, garantissant ainsi une lecture plus fluide et rapide. Pour les éditeurs de vidéo, ce nouveau comportement se traduit par des économies de bande passante. Cependant, si la recherche dépasse les limites de la mémoire tampon, le comportement standard est appliqué et le moteur d’exécution demande de nouvelles données au serveur. Remarque : ce comportement ne s’applique pas au téléchargement de vidéo progressif. Pour utiliser la recherche dynamique, définissez NetStream.inBufferSeek sur true. Dernière mise à jour le 9/5/2012 82 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau Contenu externe Divisez l’application en plusieurs fichiers SWF. Les périphériques mobiles ont parfois un accès limité au réseau. Pour charger rapidement le contenu, divisez l’application en plusieurs fichiers SWF. Si possible, réutilisez la logique de programmation et les actifs dans l’ensemble de l’application. Imaginons, par exemple, une application divisée en plusieurs fichiers SWF, comme illustré ci-après : portfolio.swf infos.swf contact.swf 10 Ko 10 Ko 10 Ko main.swf 10 Ko preload.swf Taille totale 40 ko Application divisée en plusieurs fichiers SWF Dans cet exemple, chaque fichier SWF contient sa propre copie d’un même bitmap. Il est possible d’éviter cette duplication en utilisant une bibliothèque partagée à l’exécution, comme illustré ci-dessous : portfolio.swf infos.swf contact.swf main.swf preload.swf library.swf 10 Ko Taille totale 10 ko Utilisation d’une bibliothèque partagée à l’exécution Dernière mise à jour le 9/5/2012 83 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau Une bibliothèque partagée à l’exécution est chargée pour que tous les autres fichiers SWF aient accès au bitmap. La classe ApplicationDomain stocke toutes les définitions de classe chargées et les met à disposition, lors de l’exécution, par le biais de la méthode getDefinition(). Une bibliothèque partagée à l’exécution peut également contenir toute la logique de programmation. Il est possible de mettre à jour l’application tout entière sans la recompiler. Le code suivant charge une bibliothèque partagée à l’exécution et extrait la définition que contient le fichier SWF lors de l’exécution. Vous pouvez appliquer cette technique aux polices, bitmaps, sons ou toute classe ActionScript : // Create a Loader object var loader:Loader = new Loader(); // Listen to the Event.COMPLETE event loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadingComplete ); // Load the SWF file loader.load(new URLRequest("library.swf") ); var classDefinition:String = "Logo"; function loadingComplete(e:Event ):void { var objectLoaderInfo:LoaderInfo = LoaderInfo ( e.target ); // Get a reference to the loaded SWF file application domain var appDomain:ApplicationDomain = objectLoaderInfo.applicationDomain; // Check whether the definition is available if ( appDomain.hasDefinition(classDefinition) ) { // Extract definition var importLogo:Class = Class ( appDomain.getDefinition(classDefinition) ); // Instantiate logo var instanceLogo:BitmapData = new importLogo(0,0); // Add it to the display list addChild ( new Bitmap ( instanceLogo ) ); } else trace ("The class definition " + classDefinition + " is not available."); } Pour faciliter l’extraction de la définition, chargez les définitions de classe dans le domaine d’application du fichier SWF concerné : Dernière mise à jour le 9/5/2012 84 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau // Create a Loader object var loader:Loader = new Loader(); // Listen to the Event.COMPLETE event loader.contentLoaderInfo.addEventListener ( Event.COMPLETE, loadingComplete ); // Load the SWF file loader.load ( new URLRequest ("rsl.swf"), new LoaderContext ( false, ApplicationDomain.currentDomain) ); var classDefinition:String = "Logo"; function loadingComplete ( e:Event ):void { var objectLoaderInfo:LoaderInfo = LoaderInfo ( e.target ); // Get a reference to the current SWF file application domain var appDomain:ApplicationDomain = ApplicationDomain.currentDomain; // Check whether the definition is available if (appDomain.hasDefinition( classDefinition ) ) { // Extract definition var importLogo:Class = Class ( appDomain.getDefinition(classDefinition) ); // Instantiate it var instanceLogo:BitmapData = new importLogo(0,0); // Add it to the display list addChild ( new Bitmap ( instanceLogo ) ); } else trace ("The class definition " + classDefinition + " is not available."); } Pour utiliser les classes que contient le fichier SWF chargé, il suffit maintenant d’appeler la méthode getDefinition() sur le domaine d’application actif. Vous pouvez également accéder aux classes par appel de la méthode getDefinitionByName(). Parce qu’elle charge les polices et les actifs de grande taille une seule fois, cette technique permet d’économiser de la bande passante. Les actifs ne sont jamais exportés dans d’autres fichiers SWF. Une seule restriction : il est nécessaire de tester et d’exécuter l’application par le biais du fichier loader.swf. Ce fichier charge d’abord les actifs, puis les différents fichiers SWF qui constituent l’application. Erreurs d’entrée/sortie Prévoyez des gestionnaires d’événement et des messages d’erreur pour les erreurs d’entrée/sortie. Sur un périphérique mobile, le réseau n’est pas nécessairement aussi fiable que sur un ordinateur de bureau relié à une connexion Internet haut débit. Sur ce type de périphérique, l’accès au contenu mobile externe est soumis à deux contraintes : la disponibilité et la vitesse. Veillez donc à créer des actifs légers et à ajouter des gestionnaires pour chaque événement IO_ERROR afin d’alerter l’utilisateur. Dernière mise à jour le 9/5/2012 85 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau Prenons un exemple : un utilisateur qui consulte votre site Web à partir de son périphérique mobile perd soudain sa connexion au réseau entre deux stations de métro. Un actif dynamique était en cours de chargement à ce moment-là. Ce cas de figure étant extrêmement rare sur un ordinateur de bureau, vous pouvez utiliser un écouteur d’événements vides pour empêcher l’affichage d’une erreur d’exécution. Sur un périphérique mobile, en revanche, un simple écouteur d’événements vides ne suffit pas pour gérer la situation. Le code suivant ne répond pas à une erreur d’entrée/sortie. Ne l’utilisez pas tel qu’il se présente : var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onComplete ); addChild( loader ); loader.load( new URLRequest ("asset.swf" ) ); function onComplete( e:Event ):void { var loader:Loader = e.currentTarget.loader; loader.x = ( stage.stageWidth - e.currentTarget.width ) >> 1; loader.y = ( stage.stageHeight - e.currentTarget.height ) >> 1; } Il est préférable de gérer un tel échec et de présenter un message d’erreur à l’utilisateur. Le code suivant gère correctement ce cas de figure : var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener ( Event.COMPLETE, onComplete ); loader.contentLoaderInfo.addEventListener ( IOErrorEvent.IO_ERROR, onIOError ); addChild ( loader ); loader.load ( new URLRequest ("asset.swf" ) ); function onComplete ( e:Event ):void { var loader:Loader = e.currentTarget.loader; loader.x = ( stage.stageWidth - e.currentTarget.width ) >> 1; loader.y = ( stage.stageHeight - e.currentTarget.height ) >> 1; } function onIOError ( e:IOErrorEvent ):void { // Show a message explaining the situation and try to reload the asset. // If it fails again, ask the user to retry when the connection will be restored } Il est recommandé de proposer à l’utilisateur un moyen de charger à nouveau le contenu. Vous pouvez implémenter ce comportement dans le gestionnaire onIOError(). Flash Remoting Utilisez Flash Remoting et AMF pour optimiser les échanges de données client-serveur. Vous pouvez charger du contenu distant dans des fichiers SWF à l’aide de XML. Toutefois, XML est constitué de texte brut que le moteur d’exécution charge et analyse. Il est particulièrement adapté aux applications qui chargent un contenu de petite taille. Si vous développez une application chargeant un contenu volumineux, envisagez d’utiliser la technologie Flash Remoting et le format AMF (Action Message Format). Dernière mise à jour le 9/5/2012 86 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau AMF est un format binaire par le biais duquel un serveur et le moteur d’exécution peuvent partager des données. Il réduit la taille des données et améliore la vitesse de transmission. AMF étant un format natif du moteur d’exécution, l’envoi de données AMF au moteur d’exécution permet d’éviter la sérialisation et la désérialisation, opérations gourmandes en mémoire côté client. La passerelle Flash Remoting se charge d’exécuter ces tâches. Lors de l’envoi d’un type de données ActionScript à un serveur, la passerelle gère la sérialisation du côté serveur. Elle vous envoie également le type de données correspondant. Ce type de données est une classe créée sur le serveur qui expose un ensemble de méthodes qu’il est possible d’appeler à partir du moteur d’exécution. Parmi les passerelles Flash Remoting, citons ZendAMF, FluorineFX, WebORB et BlazeDS, une passerelle Flash Remoting Java open source officielle proposée par Adobe. La figure ci-après illustre le concept Flash Remoting : ZendAMF HTTP Web ORB RubyAMF AMF FluorineFX Service (PHP Class, Java, C# ...) BlazeDS Flash Remoting Dans l’exemple suivant, la classe NetConnection établit une connexion à une passerelle Flash Remoting : // Create the NetConnection object var connection:NetConnection = new NetConnection (); // Connect to a Flash Remoting gateway connection.connect ("http://www.yourserver.com/remotingservice/gateway.php"); // Asynchronous handlers for incoming data and errors function success ( incomingData:* ):void { trace( incomingData ); } function error ( error:* ):void { trace( "Error occured" ); } // Create an object that handles the mapping to success and error handlers var serverResult:Responder = new Responder (success, error); // Call the remote method connection.call ("org.yourserver.HelloWorld.sayHello", serverResult, "Hello there ?"); La connexion à une passerelle Flash Remoting est simple. Il est toutefois possible de simplifier l’utilisation de Flash Remoting en faisant appel à la classe RemoteObject, qui figure dans le kit de développement SDK d’Adobe® Flex®. Remarque : vous pouvez utiliser des fichiers SWC externes, tels ceux issus de Flex framework, dans un projet Adobe® Flash® Professional. Grâce à ces fichiers, vous pouvez vous servir de la classe RemoteObject et de ses dépendances sans recourir au reste du kit SDK de Flex. Les développeurs expérimentés peuvent même communiquer directement avec une passerelle Flash Remoting par le biais de la classe Socket brute, le cas échéant. Dernière mise à jour le 9/5/2012 87 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Optimisation de l’interaction avec le réseau Opérations de réseau superflues Mettez en cache les actifs localement après leur chargement, au lieu de les charger à partir du réseau chaque fois qu’il est nécessaire d’y accéder. Si l’application charge des actifs (contenu multimédia ou données, par exemple), enregistrez-les sur le périphérique local pour les mettre en cache. Si les actifs ne changent pas souvent, envisagez de mettre à jour la mémoire cache à fréquence régulière. L’application peut, par exemple, rechercher une nouvelle version d’un fichier image une fois par jour, ou de nouvelles données toutes les deux heures. Vous disposez de plusieurs méthodes pour mettre en cache les actifs, selon leur type et leur nature : • Actifs multimédias (images et vidéo, par exemple) : enregistrez les fichiers dans le système de fichiers à l’aide des classes File et FileStream. • Valeurs de données individuelles ou petits ensembles de données : enregistrez les valeurs en tant qu’objets partagés locaux à l’aide de la classe SharedObject. • Ensembles de données plus volumineux : enregistrez les données dans une base de données locale ou sérialisez-les et enregistrez-les dans un fichier. Pour plus d’informations sur la mise en cache des valeurs de données, voir open-source AS3CoreLib project (disponible en anglais uniquement). Ce projet comprend une classe ResourceCache qui effectue les opérations de chargement et de mise en cache. Dernière mise à jour le 9/5/2012 88 89 Chapitre 7 : Utilisation des données multimédias Vidéo Pour plus d’informations sur l’optimisation de la vidéo sur les périphériques mobiles, voir l’article Optimize web content for mobile delivery (disponible en anglais uniquement) sur le site Adobe Developer Connection. Voir en particulier les sections suivantes : • Playing video on mobile devices • Code samples Ces sections contiennent des informations sur le développement de lecteurs vidéo pour les périphériques mobiles, telles que : • Directives d’encodage vidéo • Normes de bonne pratique • Méthode de profilage des performances du lecteur vidéo • Implémentation d’un lecteur vidéo de référence StageVideo Utilisez la classe StageVideo si vous souhaitez recourir à l’accélération matérielle pour présenter la vidéo. Pour plus d’informations sur l’utilisation de l’objet StageVideo, voir Utilisation de la classe StageVideo pour la présentation par accélération matérielle dans le Guide du développeur d’ActionScript 3.0. Audio A partir de Flash Player 9.0.115.0 et d’AIR 1.0, le moteur d’exécution prend en charge la lecture des fichiers AAC (AAC Main, AAC LC et SBR). Il est possible de réaliser une optimisation simple en utilisant des fichiers AAC plutôt que des fichiers MP3. A débits égaux, le format AAC garantit une meilleure qualité et des fichiers moins volumineux que le format MP3. La réduction de la taille de fichier permet d’économiser de la bande passante, facteur important sur les périphériques mobiles qui n’offrent pas des connexions Internet haut débit. Décodage audio via le matériel Similaire au décodage vidéo, le décodage audio sollicite fortement l’unité centrale. Il est possible de l’optimiser en tirant parti du matériel disponible sur le périphérique. Flash Player 10.1 et AIR 2.5 peuvent détecter et utiliser les pilotes audio matériels en vue d’améliorer les performances lors du décodage de fichiers AAC (profils LC, HE/SBR) ou mp3 (PCM n’est pas pris en charge). L’unité centrale est alors beaucoup moins sollicitée et donc disponible pour d’autres opérations. La batterie est elle aussi moins utilisée. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Utilisation des données multimédias Remarque : lors de l’utilisation du format AAC, le profil MP (Main Profile) AAC n’est pas géré sur les périphériques, car les pilotes matériels requis ne sont pas pris en charge sur la plupart de ces derniers. Le décodage de l’audio est transparent pour l’utilisateur et le développeur. Lorsque le moteur d’exécution commence à lire les flux audio, il vérifie d’abord le matériel, comme il le fait pour la vidéo. Si un pilote matériel est disponible et le format audio pris en charge, le décodage audio a lieu. Toutefois, même lorsqu’il est possible de gérer le décodage du flux AAC ou mp3 entrant via le matériel, il arrive que celui-ci ne puisse pas traiter tous les effets. Il arrive parfois, par exemple, que ses limitations ne permettent pas de traiter le mixage et le rééchantillonnage. Dernière mise à jour le 9/5/2012 90 91 Chapitre 8 : Performances de la base de données SQL Structure d’application visant à améliorer les performances de la base de données Ne modifiez pas la propriété text d’un objet SQLStatement après son exécution. Utilisez plutôt une occurrence de SQLStatement pour chaque instruction SQL et définissez des valeurs différentes par le biais de paramètres d’instruction. Avant d’exécuter une instruction SQL, l’environnement d’exécution la prépare (la compile) pour déterminer les étapes effectuées en interne pour l’exécuter. Lorsque vous appelez SQLStatement.execute() sur une occurrence de SQLStatement qui n’a encore jamais été exécutée, l’instruction est automatiquement préparée avant son exécution. Lors des prochains appels à la méthode execute(), et tant que la propriété SQLStatement.text ne change pas, l’instruction est déjà préparée. Son exécution est donc plus rapide. Pour optimiser au maximum la réutilisation d’une instruction, si des valeurs doivent être modifiées entre ses exécutions, personnalisez-la à l’aide de paramètres d’instruction (qui sont spécifiés à l’aide de la propriété de tableau associatif SQLStatement.parameters) . Contrairement à la modification de la propriété text de l’occurrence de SQLStatement, lorsque vous modifiez les valeurs des paramètres d’instruction, le moteur d’exécution n’a pas besoin de préparer à nouveau l’instruction. Lorsque vous réutilisez une occurrence de SQLStatement, l’application doit conserver une référence à cette occurrence une fois celle-ci préparée. A cet effet, déclarez la variable en tant que variable de domaine de classe et non en tant que variable de domaine de fonction. Pour ce faire, structurez l’application de sorte que l’instruction SQL soit enveloppée dans une seule classe. Un groupe d’instructions exécutées en combinaison peut également être enveloppé dans une même classe (cette technique repose sur l’utilisation du patron de conception Commande). Définies en tant que variables de membre de la classe, les occurrences persistent tant que l’occurrence de la classe enveloppe existe dans l’application. Au minimum, vous pouvez simplement définir une variable contenant l’occurrence de SQLStatement à l’extérieur d’une fonction de sorte que cette occurrence reste en mémoire. Par exemple, déclarez l’occurrence de SQLStatement en tant que variable de membre d’une classe ActionScript ou en tant que variable autre qu’une fonction dans un fichier JavaScript. Vous pouvez ensuite définir les valeurs de paramètres de l’instruction et appeler sa méthode execute() lorsque vous souhaitez véritablement exécuter la requête. Utilisez des index de base de données pour accélérer l’exécution des opérations de comparaison et de tri. Lorsque vous créez un index pour une colonne, la base de données enregistre une copie des données de celle-ci. Les données sont triées par ordre numérique ou alphabétique. La base de données est ainsi en mesure de faire correspondre des données (utilisation de l’opérateur d’égalité, par exemple) et de trier les résultats à l’aide de la clause ORDER BY, et ce, rapidement. Les index de base de données sont toujours tenus à jour, ce qui entraîne un léger ralentissement des opérations de modification des données (INSERT ou UPDATE) effectuées sur cette table. L’augmentation de la vitesse de récupération des données peut cependant être significative. En raison de ce compromis, évitez tout simplement d’indexer toutes les colonnes d’une table. Adoptez plutôt une stratégie de définition des index. Suivez les directives cidessous pour planifier votre stratégie d’indexation : • Indexez les colonnes utilisées pour la jointure des tables, dans les clauses WHERE ou ORDER BY. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de la base de données SQL • Si vous utilisez fréquemment des colonnes ensemble, placez-les dans un même index. • Spécifiez le classement COLLATE NOCASE pour l’index d’une colonne contenant des données texte que vous récupérez en ordre alphabétique. Envisagez de pré-compiler les instructions SQL pendant les périodes d’inactivité de l’application. Lors de sa première exécution, une instruction SQL est plus lente, car le texte SQL est préparé (compilé) par le moteur de base de données. Comme la préparation et l’exécution d’une instruction peut être une opération exigeante, il est judicieux de précharger les données initiales, puis d’exécuter les autres instructions en arrière-plan : 1 Chargez les données dont l’application a d’abord besoin.. 2 Exécutez les autres instructions au terme des opérations de démarrage initial de l’application ou lors d’une autre période « d’inactivité » de celle-ci. Supposons, par exemple, que l’application n’accède pas du tout à la base de données lors de l’affichage de son écran initial. Attendez donc que cet écran soit affiché avant d’établir la connexion à la base de données. Enfin, créez les occurrences de SQLStatement et exécutez toutes celles qui sont disponibles. A l’inverse, supposons que l’application affiche immédiatement certaines données à son démarrage, par exemple le résultat d’une requête particulière. Dans ce cas, continuez et exécutez l’occurrence de SQLStatement pour cette requête. Dès que les données initiales sont chargées et affichées, créez des occurrences de SQLStatement pour les autres opérations de base de données et, si possible, exécutez ultérieurement les autres instructions nécessaires. En pratique, si vous réutilisez des occurrences de SQLStatement, le système ne doit consacrer du temps supplémentaire à la préparation de l’instruction qu’une seule fois. L’impact sur les performances globales est probablement mineur. Groupez plusieurs opérations de modification des données SQL dans une même transaction. Supposons que vous exécutiez un grand nombre d’instructions SQL impliquant l’ajout ou la modification de données (instructions INSERT ou UPDATE). Vous pouvez augmenter significativement les performances en exécutant toutes les instructions dans une transaction explicite. Si vous ne commencez pas une transaction de façon explicite, chacune des instructions s’exécute dans sa propre transaction créée automatiquement. A l’issue de l’exécution de chaque transaction (chaque instruction), le moteur d’exécution écrit les données résultantes dans le fichier de la base de données sur disque. Regardez à présent ce qu’il se passe si vous créez explicitement une transaction et exécutez les instructions dans le contexte de cette transaction. L’environnement d’exécution effectue toutes les modifications en mémoire, puis écrit simultanément toutes les modifications dans le fichier de la base de données lorsque la transaction est validée. L’écriture de données sur le disque est généralement la partie la plus longue de l’opération. Par conséquent, écrire sur le disque une seule fois plutôt qu’une fois par instruction SQL peut améliorer significativement les performances. Lorsque les résultats de la requête SELECT sont volumineux, traitez-les par blocs à l’aide des méthodes execute() (avec le paramètre prefetch) et next() de la classe SQLStatement. Supposons que vous exécutiez une instruction SQL qui extrait un jeu de résultats volumineux. L’application traite ensuite chaque ligne de données dans une boucle. Elle formate les données ou s’en sert pour créer des objets, par exemple. Le traitement des données est susceptible d’être long, ce qui peut entraîner des problèmes de rendu (écran bloqué ou non réactif, par exemple). Pour parer à ces écueils, vous pouvez diviser les tâches par blocs (voir « Opérations asynchrones » à la page 76). L’API de la base de données SQL facilite la division du traitement des données. La méthode execute() de la classe SQLStatement possède un paramètre facultatif, prefetch (le premier). Si vous le définissez, il spécifie le nombre de lignes de résultats que renvoie la base de données au terme de l’exécution : Dernière mise à jour le 9/5/2012 92 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de la base de données SQL dbStatement.addEventListener(SQLEvent.RESULT, resultHandler); dbStatement.execute(100); // 100 rows maximum returned in the first set Une fois le premier jeu de résultats renvoyé, vous pouvez appeler la méthode next() pour poursuivre l’exécution de l’instruction et extraire un autre jeu de lignes de résultat. A l’instar de la méthode execute(), la méthode next() gère le paramètre prefetch, qui permet de spécifier le nombre maximal de lignes à renvoyer : // This method is called when the execute() or next() method completes function resultHandler(event:SQLEvent):void { var result:SQLResult = dbStatement.getResult(); if (result != null) { var numRows:int = result.data.length; for (var i:int = 0; i < numRows; i++) { // Process the result data } if (!result.complete) { dbStatement.next(100); } } } Vous pouvez continuer d’appeler la méthode next() jusqu’à ce que toutes les données soient chargées. Comme l’illustre l’exemple précédent, vous pouvez déterminer ce moment en vérifiant la propriété completede l’objet SQLResult, qui est créée chaque fois que la méthode execute() ou next() prend fin. Remarque : utilisez le paramètre prefetch et la méthode next() pour diviser le traitement des résultats d’une requête. En revanche, ne vous en servez pas pour obtenir un sous-ensemble de résultats. Si seul un sous-jeu des résultats d’une instruction vous intéresse, utilisez la clause LIMIT de l’instruction SELECT. Si le jeu de résultats est volumineux, rien ne vous empêche de diviser son traitement à l’aide du paramètre prefetch et de la méthode next(). Envisagez d’utiliser plusieurs objets SQLConnection asynchrones avec une seule base de données pour exécuter simultanément plusieurs instructions. Lorsqu’un objet SQLConnection est connecté à une base de données à l’aide de la méthode openAsync(), il s’exécute en arrière-plan et non dans le thread d’exécution principal. En outre, chaque objet SQLConnection s’exécute dans son propre thread en arrière-plan. Si vous utilisez plusieurs objets SQLConnection, vous pouvez exécuter simultanément plusieurs instructions SQL de manière efficace. Cette technique présente néanmoins quelques inconvénients. En particulier, chaque objet SQLConnection supplémentaire requiert de la mémoire. En outre, les exécutions simultanées sollicitent toujours plus le processeur, surtout sur les machines dotées d’une unité centrale ou d’un cœur d’unité centrale unique. C’est pourquoi cette technique n’est pas recommandée sur les périphériques mobiles. Autre inconvénient, vous risquez de perdre les avantages que présente potentiellement cette technique, car un objet SQLStatement est lié à un objet SQLConnection unique. Il est donc impossible de réutiliser l’objet SQLStatement si l’objet SQLConnection associé est en cours d’utilisation. Si vous décidez d’utiliser plusieurs objets SQLConnection connectés à une seule base de données, souvenez-vous que chacun d’eux exécute ses instructions dans une transaction qui lui est propre. Vous devrez tenir compte de ce facteur dans tout code qui effectue des modifications (ajout, modification ou suppression de données, par exemple). Dernière mise à jour le 9/5/2012 93 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de la base de données SQL Paul Robertson a créé une bibliothèque de code de type open source qui permet de bénéficier des avantages liés à l’utilisation de plusieurs objets SQLConnection, tout en réduisant au minimum les inconvénients potentiels. Cette bibliothèque utilise un pool d’objets SQLConnection et gère les objets SQLStatement associés. Elle garantit que les objets SQLStatement sont ainsi réutilisés et que plusieurs objets SQLConnection sont disponibles pour exécuter simultanément plusieurs instructions. Pour plus d’informations et pour télécharger la bibliothèque, aller à http://probertson.com/projects/air-sqlite/ (disponible en anglais uniquement). Optimisation des fichiers de base de données Evitez de modifier le schéma de la base de données. Dans la mesure du possible, évitez de modifier le schéma (structure des tables) d’une base de données après avoir ajouté des données dans ses tables. Normalement, un fichier de base de données est structuré avec les définitions de ses tables au début du fichier. Lorsque vous ouvrez une connexion à une base de données, l’environnement d’exécution charge ces définitions. Lorsque vous ajoutez des données dans les tables d’une base de données, ces données sont ajoutées dans le fichier après les données de définition des tables. Cependant si vous modifiez le schéma, les nouvelles données de définition de la table sont mélangées aux données de celle-ci dans le fichier de base de données. L’insertion d’une colonne à une table ou l’ajout d’une table, par exemple, peuvent entraîner le mélange des types de données. Si les données de définition de la table ne figurent pas toutes au début du fichier de base de données, il faut plus longtemps pour établir une connexion à la base de données. En effet, il faut plus longtemps au moteur d’exécution pour lire les données de définition de la table aux différents emplacements du fichier. Pour optimiser une base de données après avoir modifié son schéma, utilisez la méthode SQLConnection.compact(). Si vous devez modifier le schéma, vous pouvez appeler la méthode SQLConnection.compact() à l’issue des modifications. Cette opération restructure le fichier de la base de données de sorte que les données de définition des tables soient regroupées au début du fichier. L’opération compact() peut cependant se révéler assez longue, en particulier au fur et à mesure que la taille du fichier de la base de données augmente. Traitement superflu de la base de données à l’exécution Utilisez un nom de table complet (comprenant le nom de la base de données) dans l’instruction SQL. Spécifiez systématiquement le nom de la base de données conjointement avec chaque nom de table dans une instruction. (Utilisez « main » s’il s’agit de la base de données principale.) Le code suivant inclut le nom de base de données explicite main : SELECT employeeId FROM main.employees Le fait de spécifier de façon explicite le nom de la base de données évite au moteur d’exécution d’avoir à rechercher la table correspondante dans chaque base de données connectée. Cela lui évite également toute possibilité de se tromper de base de données. Respectez cette règle même lorsque l’occurrence de SQLConnection n’est connectée qu’à une seule base de données. En effet, en arrière-plan, l’occurrence de SQLConnection est également connectée à une base de données temporaire accessible par l’intermédiaire d’instructions SQL. Dernière mise à jour le 9/5/2012 94 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de la base de données SQL Utilisez des noms de colonne explicites dans les instructions SQL INSERT et SELECT. Les exemples suivants illustrent l’utilisation de noms de colonne explicites : INSERT INTO main.employees (firstName, lastName, salary) VALUES ("Bob", "Jones", 2000) SELECT employeeId, lastName, firstName, salary FROM main.employees Comparez les exemples précédents aux suivants. Evitez ce style de code : -- bad because column names aren't specified INSERT INTO main.employees VALUES ("Bob", "Jones", 2000) -- bad because it uses a wildcard SELECT * FROM main.employees S’ils ne sont pas explicites, il est plus difficile pour le moteur d’exécution d’identifier les noms de colonne. Si une instruction SELECT utilise un caractère générique au lieu de colonnes explicites, le moteur d’exécution récupère plus de données. Ces données doivent être traitées et engendrent des occurrences d’objet inutiles. Evitez de joindre une même table plusieurs fois dans une même instruction, à moins que vous ne la compariez à ellemême. Au fur et à mesure que les instructions SQL croissent, il peut vous arriver, par mégarde, de joindre plusieurs fois une table de base de données à la requête. Vous pourriez souvent obtenir le même résultant en utilisant la table à une seule reprise. Vous êtes susceptible de joindre une même table plusieurs fois si vous utilisez une ou plusieurs vues dans une requête. Vous pourriez, par exemple, joindre une table à une requête, ainsi qu’une vue contenant les données de la table. Ces deux opérations donneraient lieu à plusieurs jointures. Syntaxe SQL performante Pour inclure une table dans une requête, utilisez l’instruction JOIN (dans la clause FROM) plutôt qu’une sous-requête dans la clause WHERE. Ce conseil est valable même si vous souhaitez utiliser les données de la table à des fins de filtrage, pas dans le jeu de résultats. Il est plus efficace d’effectuer une jointure de plusieurs tables dans la clause FROM que d’utiliser une sous-requête dans la clause WHERE. Evitez les instructions SQL qui ne tirent pas parti des index. Il s’agit d’instructions utilisant des fonctions agrégées dans une sous-requête, d’une instruction UNION dans une sous-requête ou d’une clause ORDER BY conjuguée à une instruction UNION. Un index peut grandement accélérer le traitement d’une requête SELECT. Certaines syntaxes SQL, cependant, empêchent la base de données d’exploiter les index, la forçant à effectuer les opérations de recherche et de tri sur les données elles-mêmes. Dans la mesure du possible, évitez l’opérateur LIKE, surtout avec un caractère générique à gauche, comme dans LIKE('%XXXX%'). Dernière mise à jour le 9/5/2012 95 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Performances de la base de données SQL L’opérateur LIKE prend en charge les recherches par caractères génériques. Il est donc plus lent que les comparaisons exactes. En particulier, si la chaîne de recherche commence par un caractère générique, la base de données ne peut absolument pas utiliser les index dans la recherche. Elle est obligée d’examiner tout le texte de chaque ligne de la table. Dans la mesure du possible, évitez l’opérateur IN. Si vous connaissez d’avance les valeurs possibles, il est possible de remplacer l’opérateur IN par AND ou OR pour accélérer l’exécution. La seconde des deux instructions suivantes s’exécute plus vite, car elle utilise des expressions d’égalité simples conjuguées à OR, et non les instructions IN() ou NOT IN() : -- Slower SELECT lastName, firstName, salary FROM main.employees WHERE salary IN (2000, 2500) -- Faster SELECT lastName, firstName, salary FROM main.employees WHERE salary = 2000 OR salary = 2500 Envisagez d’utiliser d’autres formes d’une instruction SQL pour améliorer les performances. Comme illustré dans les exemples précédents, la syntaxe d’une instruction SQL peut affecter les performances de la base de données. Il existe souvent plusieurs manières d’écrire une instruction SQL SELECT pour récupérer un jeu de résultats particulier. Une technique s’exécutera parfois sensiblement plus vite qu’une autre. Outre les suggestions précédentes, vous trouverez des informations supplémentaires sur les différentes instructions SQL et leurs performances dans les ressources dédiées du langage SQL. Performances des instructions SQL Comparez directement différentes versions d’une instruction SQL pour déterminer la plus rapide. Pour comparer les performances de plusieurs versions d’une instruction SQL, le mieux consiste à les tester directement avec la base de données et les données. Les outils de développement suivants indiquent les durées d’exécution des instructions SQL. Servez-vous en pour comparer la vitesse des différentes versions des instructions : • Run! (Outil de test et de création de requêtes SQL AIR, développé par Paul Robertson. Disponible en anglais uniquement.) • Lita (SQLite Administration Tool, développé par David Deraedt. Disponible en anglais uniquement.) Dernière mise à jour le 9/5/2012 96 97 Chapitre 9 : Test de performances et déploiement Test de performances Un certain nombre d’outils permettent de tester les performances des applications. Vous pouvez utiliser les classes Stats et PerformanceTest, développées par des membres de la communauté Flash, ainsi que le profileur d’Adobe® Flash® Builder™ et l’outil FlexPMD. Classe Stats Pour tester votre code à l’exécution à l’aide de la version commerciale du moteur d’exécution, sans outil externe, vous pouvez utiliser la classe Stats développée par mr. doob de la communauté Flash. Vous pouvez télécharger la classe Stats à l’adresse suivante : https://github.com/mrdoob/Hi-ReS-Stats. La classe Stats permet d’assurer le suivi des éléments ci-dessous : • Nombre d’images rendues par seconde (plus ce nombre est élevé, mieux c’est). • Durée de rendu d’une image, en millisecondes (plus ce nombre est faible, mieux c’est). • Quantité de mémoire utilisée par le code. Si cette quantité augmente à chaque image, il est possible que votre application ait une fuite de mémoire. Il est important d’identifier ce problème potentiel. • Quantité maximale de mémoire utilisée par l’application. Une fois la classe Stats téléchargée, vous pouvez l’utiliser avec le code compact ci-dessous : import net.hires.debug.*; addChild( new Stats() ); En utilisant la compilation conditionnelle dans Adobe® Flash® Professional ou Flash Builder, vous pouvez activer l’objet Stats : CONFIG::DEBUG { import net.hires.debug.*; addChild( new Stats() ); } Il vous suffit de basculer la valeur de la constante DEBUG pour activer ou désactiver la compilation de l’objet Stats. Vous pouvez appliquer cette technique pour remplacer toute logique de code que vous ne souhaitez pas compiler dans votre application. Classe PerformanceTest Pour profiler l’exécution du code ActionScript, Grant Skinner a développé un outil qu’il est possible d’intégrer à un flux de travail de test unitaire. Vous transmettez une classe personnalisée à la classe PerformanceTest, qui exécute une série de tests sur le code. La classe PerformanceTest facilite le test des performances de différentes techniques. Vous pouvez télécharger la classe PerformanceTest à l’adresse suivante : http://www.gskinner.com/blog/archives/2009/04/as3_performance.html. Dernière mise à jour le 9/5/2012 OPTIMISATION DES PERFORMANCES POUR LA PLATE-FORME ADOBE FLASH Test de performances et déploiement Profileur Flash Builder Flash Builder comprend un profileur permettant de tester les performances de votre code, de manière très détaillée. Remarque : pour accéder à ce profileur, utilisez la version de débogage de Flash Player, sans quoi vous obtiendrez un message d’erreur. Le profileur s’utilise également avec du contenu créé dans Adobe Flash Professional. A cet effet, chargez dans Flash Builder le fichier SWF compilé, depuis un projet ActionScript ou Flex, et exécutez le profileur sur le fichier. Pour plus d’informations sur le profileur, voir « Profilage des applications Flex » dans le guide Utilisation de Flash Builder 4. FlexPMD Les services techniques Adobe offrent un outil, appelé FlexPMD, qui permet d’évaluer la qualité du code ActionScript 3.0. FlexPMD est un outil ActionScript, similaire à JavaPMD. FlexPMD améliore la qualité du code ActionScript en évaluant un répertoire source ActionScript 3.0 ou Flex. Il détecte les mauvaises pratiques de programmation, telles que le code inutilisé, trop complexe ou trop long et l’utilisation incorrecte du cycle de vie des composants Flex. FlexPMD est un projet Open Source d’Adobe disponible à l’adresse suivante : http://opensource.adobe.com/wiki/display/flexpmd/FlexPMD. Un module d’extension Eclipse est également disponible à l’adresse suivante : http://opensource.adobe.com/wiki/display/flexpmd/FlexPMD+Eclipse+plugin. FlexPMD facilite l’évaluation du code et permet de s’assurer qu’il est correct et optimisé. FlexPMD est un outil d’autant plus puissant qu’il est extensible. En tant que développeur, vous pouvez créer vos propres jeux de règles pour évaluer tout code. Libre à vous, par exemple, de créer un jeu de règles pour détecter l’utilisation excessive de filtres ou toute autre mauvaise pratique que vous souhaitez identifier. Déploiement Lors de l’exportation de la version définitive de votre application dans Flash Builder, vous devez vous assurer qu’il s’agit bien de la version commerciale (validée). L’exportation d’une version commerciale supprime toutes les informations de débogage du fichier SWF. Le fichier SWF est ainsi moins volumineux et l’application s’exécute plus rapidement. Pour exporter la version commerciale de votre projet, accédez au panneau Projet de Flash Builder et sélectionnez l’option Exporter vers une version validée. Remarque : lors de la compilation du projet dans Flash Professional, il est impossible de choisir entre la version de débogage et la version commerciale. Le fichier SWF compilé est, par défaut, une version commerciale. Dernière mise à jour le 9/5/2012 98