Hardening consulting

Jouons avec Wayland

Un petit article suite à une erreur que j'ai faite en codant UWAC (Using Wayland As Client), ma librairie pour faire du wayland coté client dans l'esprit de la Xlib. Les symptômes étaient les suivants: une application se servant d'UWAC fonctionnait bien avec le X11 compositor et freezait avec le RDP compositor. Après des tests, il s'est avéré que c'est le pixman renderer qui semblait induire ça (en utilisant le X11 compositor et en se servant du pixman renderer, on avait le même comportement).

Pools, buffers, frames et compagnie...

Avant de rentrer dans l'analyse, un petit rappel de comment ça marche wayland. Si vous êtes complètement novice du sujet, la lecture de cet article pourrait vous éclairer.

Pools et buffers

Dans mon cas, il s'agissait d'un rendu en utilisant wl_shm, c'est à dire que la communication entre le client wayland et le compositor se fait en utilisant une mémoire partagée.

Pour le client ça consiste à:

  • créer un segment de mémoire partagée, par exemple en utilisant mmap sur un "fichier anonyme";
  • créer un wl_shm_pool en appelant wl_shm_create_pool() en passant le descripteur de fichier et la taille qu'on souhaite utiliser du fichier;
  • une fois le pool créé, on peut allouer des wl_buffer dans ce pool en appelant wl_shm_pool_create_buffer.

Surfaces, frames

On dispose de buffers dans lesquels on peut dessiner, les étapes suivantes vont consister à attacher le buffer à une surface (wl_surface_attach), notifier les zones modifiées (wl_surface_damage), et finalement on notifie au compositeur qu'il peut se servir du buffer (wl_surface_commit) pour faire une frame.

Théoriquement, quand on a attaché un buffer à une surface et qu'on a demandé le rendu d'une frame, on ne peut plus toucher au buffer; quand le compositeur a fini de s'en servir pour faire son rendu, il envoit un event release, ce qui permet de s'en reservir.

En appelant wl_surface_frame, on va pouvoir être prévenu quand la frame a été affichée, on recevra un event de type done.


Le bug

Dans UWAC, j'avais implémenté un triple buffering, j'avais prévu un buffer courant dans lequel dessiner, un buffer qui aurait été déjà envoyé au compositeur. Et enfin, un buffer pour une frame qui aurait été créée, mais qui serait en attente d'envoi coté client (en attente du message frame done). Donc, j'allouais statiquement à l'initialisation du display 3 wl_buffer, et après je les faisait tourner.

Il se trouve que je me servais du message de frame finie (wl_surface.done) pour notifier l'utilisateur de la librairie UWAC que la frame avait été dessinée et qu'il pouvait reprogrammer une frame. Par contre, ma gestion des buffers était dirigée par la reception du message wl_buffer.release: j'attendais que le compositor me dise qu'un buffer n'était plus utilisé pour les faire tourner et donc passer à l'image suivante.

C'est là que ce trouvait mon bug: le compositeur peut très bien ne pas envoyer tout de suite le message wl_buffer.release. Dans le cas de l'utilisation du pixman renderer, le buffer est conservé jusqu'à ce que le compositor n'en n'ai plus l'usage, et avec ce renderer c'est quand on soumet une autre frame frame. En discutant, on m'a cité comme exemple un compositeur qui ferait 24 images secondes et garderais 24 wl_buffers.

Conclusion

J'ai changé mon implémentation, maintenant les wl_buffers sont alloués dynamiquement, et la génération des images est bien dirigée par le signal frame done. Si le compositeur ne rend pas le buffer assez vite, on en alloue un autre. Je m'attendais à avoir une augmentation significative du nombre de buffers et en fait on reste à 3

Merci à Derek Foreman qui m'a donné la solution à mon bug.