Je vais passer en revu et commenter différentes choses. Je tenterai, dans les parties suivantes, d’établir quelques règles et guides à suivre puis je finirai sur une explication de l’organisation du code. :banaeyouhou:

Terminologie

Avant de commencer je dois expliciter certains termes que je vais employer. :redface:

Job manager

C’est le cerveau. Il s’agit du « serveur » :

  • Il stocke tous les jobs.
  • Il gère les dépendances entre les jobs en les envoyant dans le bon ordre et en attendant certains jobs avant d’en exécuter d’autres.
  • Il communique avec les workers en leur envoyant les jobs.
  • Il met à jour les statuts (pause/en cours/erreur).
  • Il gère les limites de licence pour s’assurer que les jobs ont toujours les licences qu’ils exigent.
  • etc.

Le job manager embarque souvent une base de données (PostgreSQL, MySQL, etc.) et ne doit jamais être indisponible pour les workers. :siffle:

Worker

« Travailleur » en français. Il s’agit d’un petit programme qui tourne et communique avec le job manager pour recevoir et exécuter des jobs. On exécute souvent un worker par machine de calcul, mais il n’est pas rare d’exécuter plusieurs workers sur une seule machine. Parfois pour des raisons de licences liées à la machine (une machine Yeti, plusieurs exports de .fur), parfois pour des raisons d’équilibre de charge. Ainsi, plusieurs petits jobs qui demandent peu de ressources peuvent s’exécuter en parallèle sur une seule machine.

Notez qu’un gestionnaire de ferme évolué permet de gérer intelligemment les ressources d’une machine et de lancer plus ou moins de jobs suivant les exigences du job (licence, mémoire, etc.).

Job

Bien que le terme français « tâche » existe, il fait souvent spécifiquement référence, en production, à la tâche du graphiste (traduction de « task », qu’on peut retrouver dans Shotgun). J’utilise donc le terme de « job » pour faire référence à ce que doit exécuter un worker.

Exigence des jobs

Parfois appelé job requirement. C’est un concept qui est présent sous différentes formes dans la plupart des job managers du marché. Le principe est que chaque job contient des informations sur ce qui est nécessaire à son exécution. Plus ces informations sont complètes, plus le job manager pourra gérer au mieux la ferme. Le cas le plus courant est le type et le nombre de licences nécessaire à l’exécution du job.

Voici un exemple d’exigence de job :

{'requirement': {'license': {'nuke': 1},
                            {'sapphire': 1}
                 'cpu_count': '12+',
                 'memory_min’: '8GB',
                 'memory_max': '64GB'}

Le dictionnaire suivant spécifie que le job :

  • Utilisera une licence Nuke et une licence Sapphire. Le job manager va donc n’assurer que ces licences sont disponibles avant le lancer le job.
  • Utilisera tous les CPU disponibles mais nécessite au minimum 12 CPU.
  • Utilisera au minimum 8 GB de RAM…
  • …mais n’utilisera jamais plus de 64 GB.

L’aspect intéressant c’est que certaines informations peuvent aider le job manager à mieux organiser la soumission des jobs. Ainsi, en spécifiant memory_max, vous « garantissez » au job manger que le job ne fera jamais plus de 64 GB. Si, par exemple, vous avez des petits jobs qui n’utilisent qu’un seul CPU vous pourriez spécifier cpu_count à 1 et le job manager saurait que votre job ne va utiliser qu’un seul CPU.

C’est une sorte de « contrat » que vous passez avec le job manager. C’est donc à double tranchant. Si vous lui dites que votre job utilisera 16 GB et qu’il monte à 64 GB, le worker aura des soucis pour l’exécuter. :nannan: Bien entendu, c’est celui qui génère le job qui spécifie ses exigences. Comme ce sont souvent les TD qui codent les chaînes de job, les exigences sont dans le code. :gniarkgniark:

Job de génération des fichiers de rendu

Le job de rendu est le type de job le plus courant. C’est souvent celui pour lequel vous souhaitez mettre une ferme de rendu en place. :baffed:

Scène de travail -> images rendues
  .mb/.gproject  |      .exr

Comme ce sont souvent les jobs les plus longs, il est préférable de séparer la génération des fichiers de rendu (.rib, .ass, etc.) et le rendu de ces derniers en deux jobs distincts :

Scène de travail -> fichiers de rendu -> images rendues
  .mb/.gproject  |     .rib/.ass      |      .exr

La génération des fichiers de rendu consiste à ouvrir une scène (Maya/Guerilla/Katana/Poalobra) et à faire les exports des fichiers de rendu (.rib, .ass, etc.), sur un range d’images donné.

Dans le cas de Guerilla, on ouvre le .gprojet, on exporte un range d’images (paquet de 10 par exemple : 101-110, 111-120, etc.) en .rib.

Pourquoi ne pas exporter toutes les images plutôt que par petits paquets ? Tout simplement parce que le but est de garder la durée de vos jobs aussi consistante et contrôlable que possible. :laClasse:

Si on exportait « toutes les images », on aurait des temps très aléatoires suivant les plans du fait de la disparité de leur nombre d’images (certains plans font une trentaine d’images, d’autre plusieurs centaines). Si vous avez un bug sur la dernière image sur votre plan de 900 frames (oui parce qu’on a toujours un problème sur la PUTAIN de dernière image du plan le plus long du projet ! :injures: ), il vous faudrait tout relancer. Et si l’export dure 30 minutes, vous allez passer des heures à le déboguer. :casseTeteMur:

Diviser en paquet permet de rendre le temps humainement gérable.

Dans un monde idéal et instantané, on n’exporterait qu’une seule image par job. Ceci permettrait d’avoir le job de rendu de l’image en dépendance directe sur le job de génération de fichier de rendu. Mais ce n’est pas possible, il y a un temps minimum nécessaire à l’ouverture de l’application et de la scène. :septic:

La taille des paquets dépend de la lenteur de l’ouverture de la scène (Les scènes Maya peuvent être lentes si elles ne sont pas « vides »). Plus la scène est lente à ouvrir, plus il est intéressant d’augmenter la taille du paquet pour contrebalancer le temps d’ouverture de la scène.

J’utilise, en général, des paquets de 10 images pour Guerilla et 20 pour Maya. Mais c’est vraiment suivant la taille du projet. :redface:

Notez qu’il n’est pas toujours possible et/ou faisable d’avoir des jobs de génération de fichiers de rendu sur votre ferme de calcul. Par exemple, on peut vouloir rendre des ranges d’images directement depuis son logiciel favori (Maya à une époque et je suppose Blender, Cinema 4D, etc.). Cette méthode, plus « directe » peut se révéler chaotique dès lors que des problèmes apparaissent. Il faut être vigilant. :papi:

Job de rendu

C’est sûrement le type de job le plus long de la ferme de calcul. Je conseille fortement de n’avoir qu’une image par job.

Les informations importantes de tels jobs se situent dans le log. Certains job manager permettent de parser le log à la volée pour remonter des informations de façon claire (temps de rendu, fichiers manquants, etc.) voir, arrêter le job et gagner un temps précieux.

Si les temps de rendu sont importants (12 heures et plus) et si votre moteur de rendu le permet, il peut être intéressant d’avoir accès à l’image « en l’état » avant la fin du rendu (au bout de 10 minutes par exemples) ceci permet de pouvoir s’assurer qu’aucun truc grossier n’est présent/absent du rendu et d’éviter de perdre des heures inutiles. :reflexionIntense:

La technique du pauvre consisterait à lancer deux jobs de rendu utilisant les mêmes fichiers de rendu mais avec des arguments de la ligne de commande différents :

  • Un avec une résolution faible et un faible nombre de samples ne dépassant pas 10 minutes.
  • Et un autre, le vrai de plusieurs heures.

Mais cela reste très imparfait, car quand le « vrai » rendu peut rarement partir dans les mêmes conditions que le premier. Il peut ainsi se retrouver sur une autre machine. Le meilleur reste donc d’utiliser l’image du rendu en cours. Si le moteur ne le permet pas, vous pouvez lancer une seule frame, au milieu du plan (ou une frame toutes les 5 frame par exemple) avec une priorité plus élevée. Vous n’attendez ainsi pas la fin complète des images pour en vérifier quelques-unes. Mais cette technique aussi est imparfaite, car vous ne voyez pas l’ensemble du plan, juste quelques images.

Dans les deux cas, cela vous permet de générer des vidéos de « pré-rendu », mais forcement, avec tout cela, le suivi du rendu est alourdi. :septic:

Job d’export Alembic

C’est sûrement le second job le plus courant : L’ouverture de scène puis l’export de son contenu au format Alembic.

La méthode bourrine consiste à ouvrir la scène et tout exporter dans un gros fichier .abc.

Ouverture de la scène Maya -> Export Alembic
         .mb               |       .abc

C’est lourd et complètement con. :vomit:

En pratique, il est plus pertinent de séparer les exports suivant leur nature. Ainsi, s’il y a 3 personnages dans la scène, l’animation de chaque personnage est exportée dans son Alembic respectif. Il est ainsi plus rapide de réexporter l’animation d’un seul personnage lors d’une retake.

Le plus intéressant étant encore de savoir ce que contient le plan à l’avance en vu de générer un job par personnage. :dentcasse:

Job d’export ATOM

À l’instar du rendu, plutôt que de passer directement par un job d’export Alembic, on pourrait passer par une étape intermédiaire consistant à exporter l’ATOM des contrôleurs d’animation, puis un second job, celui exportant l’Alembic, partirait d’une scène vide, amènerait une version du rig (la dernière par exemple) appliquerait l’ATOM sur les contrôleurs d’animation puis ferait l’export.

                                     Rig
                                      |
                                      v
​Scène de travail -> fichiers ATOM -> Maya -> Export anim
      .mb        |     .atom      |       |     .abc

Mais on pourrait aller encore plus loin ! :onSeFendLaPoire:

Pourquoi ne pas en profiter pour faire un preroll automatique pour nos amis du cloth et du FX ?

  • Une clé, en T-pose, à l’image 1.
  • Une clé, toujours en T-pose, mais avec le personnage placé à sa position de départ à l’image 50.
  • Un déplacement du personnage vers sa première position d’animation à l’image 70.
  • L’application de l’ATOM du début du plan (101) à la fin.
  • Export de l’Alembic.

Pensez à garder les paramètres de numéro de frame modifiables afin que les départements puisse régénérer leur Alembic avec preroll si la version automatique n’est pas optimale.

Job de génération de vidéo

Ce job s’occupe de générer des vidéos, souvent .mp4 depuis une séquence d’images (.jpeg, .exr, etc.). Ce dernier est loin d’être simple.

On peut utiliser Nuke pour bénéficier de tous les outils dont il dispose (écriture de texte, option d’export, etc.), mais Nuke n’est pas donné et nécessite une licence. :cayMal:

Le module Python Pillow est vraiment très bon. Je le recommande chaudement. :sauteJoie:

Pour les cartouches (bandes noir en haut et en bas, affichant des informations) je déconseille de calculer dynamiquement leur taille via un pourcentage. Gardez une taille fixe, connu et simple à retenir pour tout le monde (100 pixels, 150 pixels, 200 pixels, etc.).

+--------------------------------+
|text1        text4        text7 |
|text2        text5        text8 |
|text3        text6        text9 |
|--------------------------------|
|                                |
|                                |
|           *picture*            |
|                                |
|                                |
|--------------------------------|
|text10       text13       text16|
|text11       text14       text17|
|text12       text15       text18|
+--------------------------------+

Neuf infos en haut et neuf infos en bas, soit 18 petits textes d’informations ! :sourit:

Liste non exhaustive d’informations intéressantes à mettre dans les cartouches :

  • Frame : 150 (101-192).
  • Date.
  • Nom du graphiste.
  • Focale.
  • Watermark.
  • Colorimetrie.

Vous pouvez également ajouter une ou plusieurs images (cartons) en début de vidéo avec plus d’informations (commentaire du graphiste, date des fichiers utilisés, mire colorimétrique, etc.).

Job de synchro

Que l’on travaille dans un studio multi-site ou qu’on utilise les ferme de rendu dans le nuage, les jobs de synchro sont monnaie courante. Ces jobs ne prennent quasiment aucune ressource et ne font souvent rien d’autre qu’attendre. En effet, ils sont rarement chargés de faire la synchro eux-mêmes mais passe par un service dédié auquel ils envoient des informations des fichiers à synchroniser (il faudra que je fasse un billet là dessus un jour). Ensuite, ils attendent que le service de synchro leur dise que les fichiers demandés ont été synchronisés. D’où l’importance de s’assurer qu’aucun worker dédié à une tâche lourde (rendu, export, etc.) n’exécute un job de synchro. Ce serait une perte de temps énorme. Pour cela il peut être pratique d’avoir une machine composée de dizaines de worker qui ne font qu’exécuter des jobs de synchro (c’est-à-dire attendre… :seSentCon: ).

Une fois ces jobs exécutés ils « débloquent » les jobs en dépendance qui peuvent s’exécuter sur leur worker avec la garantie que les fichiers nécessaires sont présents.

Job de nettoyage

Ce job sert tout simplement à supprimer les fichiers qui ne sont plus nécessaires. Ça peut être les .rib ou des .vrmesh généré à la volée par la chaîne de job mais qui ne servaient qu’au calcul d’une image.

Souvent appelés cleanup jobs, leur mécanisme est parfois pris en charge par le job manager qui ne s’exécute qu’au moment de la suppression des jobs. On ne peut donc pas directement parler de « job » mais plutôt de cleanup list (le job contient la liste des fichiers/dossier à supprimer) :

{'cleanup':['/path/to/file.rib',
            '/path/to/folder']}

Mais tous ne le gèrent pas comme ça et un job dédié reste la méthode traditionnelle.

Il fut un temps, ces jobs supprimaient les shadow map, les point-clouds, les brick maps, etc. qui pesaient très lourd.

C’est la fin de cette première partie ! J’espère que ça vous aura intéressé. La suite, un jour… :aupoil:

:marioCours: