
Récemment, j'ai du utiliser les shaders ou effets Silverlight pour mes besoins personnels. En soit, cela est assez courant, leur utilisation peut toutefois se faire au détriment des performances de votre application. Cet article explique comment optimiser la mémoire et la charge processeur lorsque vous appliquez des effets aux instances d'UIElement.
Tout d'abord, chaque fois que vous appliquez l'un des shaders ou effets fournis par défaut, soit DropShadow et Blur, sur une instance d'UIElement, vous générez en réalité deux bitmaps en RAM à l'exécution. La première bitmap est un rendu simple des vecteurs, la seconde bitmap est l'image affichant l'effet finalisé. Celle-ci est calculée à partir de la première. En termes de performance, ce comportement, peut conduire à certains problèmes si vous ne prenez pas garde :
Il nous faut tout d'abord calculer la mémoire utilisée par un effet standard appliqué à un UIElement 200 * 200. Pour cela, nous prenons en compte que chaque pixel (40000 pour une telle image) est codé sur 4 octets en mode ARGB:
* la couche de transparence Alpha est codée sur 1 octet (appelé octet en français) et 8 bits, conduit à 256 valeurs possible de 0 à 255 ou 0x00 à 0xFF en hexadécimal. les autres couches sont sur le même modèle.
* Red | 1 octet | 8 bits | 0xFF | 0-255
* Green | 1 octet | 8 bits | 0xFF | 0-255
* Blue | 1 octet | 8 bits | 0xFF | 0-255
Les images étant générées à l'exécution, celle-ci sont stockées dans la mémoire vive. Comme elles ne sont pas compressées, il est facile de calculer leur poids via la formule ci-dessous :
PixelsWidth * PixelsHeight * 4 octets = poids total de la bitmap intermédiaire dans la mémoire RAM
Dans notre cas, le bitmap est d'environ 160 Ko, mais cela correspond uniquement au poids de l'image intermédiaire. La seconde image occupera au moins 160 k octets voir plus, pour le projeté d'ombre et le flou. Celle-ci déborde en effet de l'enveloppe afin d'afficher l'effet finalisé. le poids total est donc au minimum d'environ 400 kilo octets si l'on considère que ces effets ont un rendu de surface plus grand que l'enveloppe d'origine.
Concernant les effets personnalisés que vous créez par vous-même via des logiciels comme Shazam en utilisant le langage HLSL (High Level Shader Language), les comportements du lecteur sont assez différents car Silverlight ne génère pas d'image intermédiaire pour rendre l'effet. Ce comportement présente de grands avantages en termes d'occupation mémoire mais reste spécifique aux effets que vous développerez par vous-même. Attention toutefois au fait que ce comportement n'est plus valable lorsque vous appliquez de la 3D ou que vous mettez le vecteur en cache via la propriété CacheMode. Dans ces derniers cas, l'image intermédiaire est systématiquement créée...
Le filtre est souvent recalculé au cours du temps lors de certaines transformations du vecteur original. Seules exception, ce n'est pas le cas pour les transformations de type déplacement (TranslateTransform). Pour toutes les autres opérations, l'image intermédiaire est recréée de toute pièce, l'image finalisée l'est donc également. Par exemple, si vous décidez d'animer l'échelle, la rotation ou l'inclinaison d'un rectangle, le rendu de l'effet et les images en mémoire qui en découlent seront regénérées à chaque fois. Ce comportement permet de conserver un effet sans pixelisation ou crénelage. Par exemple, pour un raffraichissement de 60 image par seconde et une animation d'échelle, Silverlight génèrera environ 60 bitmaps intermédiaires et 60 bitmaps affichant l'effet final. Les designers et les développeurs doivent prendre garde à ce type de comportement. Dans le cas contraire, la charge mémoire et CPU utilisée pour afficher de simples effets, pourrait augmenter de manière drastique et diminuer ce faisant les performances de votre application... Pour éviter ou limiter ces ralentissements, l'équipe d'ingénieurs Silverlight a autorisé une taille maximum de 2048 pixels en largeur et en hauteur. Cette limitation n'est toutefois pas si simple à mettre en valeur car certains artefacts de rendu peuvent apparaître dans ces dimensions. Lorsqu'un effet est supprimé ou qu'un UIElement est détruit, la mémoire réservée pour rendre l'effet est automatiquement libérée.
Vous trouverez certaines des meilleures pratiques ci-dessous:
* Plus la surface sur laquelle vous appliquez un effet est petite, mieux c'est. Les effets ne doivent pas être appliqués de manière inconsidérée et sont susceptibles d'alourdir considérablement votre visuel.
* Evitez les effets sur les éléments qui subissent des animations. Mise à part dans le cas de déplacement, cela conduit à recalculer les images bitmaps et à les replacer en mémoire systématiquement selon la cadence de votre animation.
* L'effet Blur est très lent car multipasse. Conservez un faible rayon. Au delà de 10 pixels, vous commencerez à remarquer une dégradation flagrantes des performances.
Pour finir, sachez que depuis .NET 3.5, Silverlight 3 et WPF reposent tous deux sur le même langage HLSL ce qui permet d'utiliser les mêmes effets de filtre personnalisés sur les deux plateformes.