Le portage du moteur Source

Les leçons retenues par Valve

Alexandre Laurent

À la GPU Technology Conference, à San Jose, en Californie, John McDonald (NVIDIA), Rich Geldreich et Mike Satrain (Valve Software) ont présenté les leçons retenues par Valve lors du portage du moteur Source sur Linux.

Commentez

- Titre : Le portage du moteur Source

- Auteur : Alexandre Laurent

- Parution : 11 octobre 2013

- Licence : Copyright ® 2013 Alexandre Laurent. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Conférence

- III. Pourquoi avoir porté Source ?

- III-A. OpenGL

- III-A-1. Fonctionnalités

- III-A-2. Gouvernance

- IV. Quels sont les points à connaître entre Windows et Linux

- IV-A. SDL

- IV-B. Système de fichiers

- IV-C. Autres problèmes

- IV-C-1. #define

- IV-C-2. Internationalisation

- IV-C-3. Police de caractères

- IV-C-4. RDTSC

- IV-C-5. Entrées souris

- IV-C-6. Gestion des configurations multiécrans

- V. Quels sont les outils sous Linux

- V-A. CPU compilation et débogage

- V-B. Analyse de performance sur CPU

- V-B-1. Telemetry

- V-B-2. Résolution de symboles

- VI. Quels sont les points à connaître entre Direct3D et OpenGL

- VI-A. Correspondance des versions

- VI-B. Portage

- VI-B-1. Performance

- VI-B-2. Pièces importantes

- VI-B-3. Différences

- VI-B-3-a. Objets OpenGL

- VI-B-3-b. Extensions

- VI-B-3-c. Extensions utiles

- VI-B-3-d. Autres extensions intéressantes

- VI-B-4. Astuces

- VI-C. Lacunes d'OpenGL

- VI-C-1. Création d'un contexte OpenGL

- VI-D. Divers

- VI-D-1. Attributs de sommets

- VI-D-2. Mise à jour des tampons

- VI-D-3. Rendu dans une texture

- VI-D-4. Shaders

- VI-D-5. Performances

- VI-D-6. Outils

- VII. Remerciements

I - Introduction

Valve Software, le studio de développement à l'origine de jeux phares comme Half Life ou encore Team Fortress, a annoncé le portage de son moteur « Source » sur Linux. John McDonald (NVIDIA), Rich Geldreich et Mike Satrain (Valve Software) reviennent sur ce portage et ont présenté à la GPU Technology Conference à San Jose, en Californie, les raisons de ce portage et les difficultés rencontrées.

II - Conférence

Animation en format "youtube" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://www.youtube.com/watch?v=btNVfUygvio

III - Pourquoi avoir porté Source ?

Valve avance plusieurs arguments quant à la nécessité de ce portage :

- Linux est ouvert ;

- Linux se tourne vers les jeux (lentement) ;

- c'est une étape avant un portage vers les mobiles (OpenGL est très proche de OpenGL ES) ;

- les performances ;

- avoir Steam sur Linux.

III-A - OpenGL

III-A-1 - Fonctionnalités

Un avantage d'OpenGL se trouve dans son fonctionnement. En effet, les fonctionnalités sont exposées suivant les capacités du matériel et non du système d'exploitation. Ainsi, les possesseurs d'ordinateurs fonctionnant sous Windows XP (encore très présent en Chine) sont bloqués à utiliser DirectX 9 alors qu'OpenGL peut permettre des fonctionnalités similaires à ce que l'on retrouve dans DirectX 10 et 11.

III-A-2 - Gouvernance

Les spécifications sont ouvertes et consultables par tous (ici). OpenGL est géré par un comité et tout le monde peut y participer (avec un coût, mais qui reste léger).

IV - Quels sont les points à connaître entre Windows et Linux

IV-A - SDL

Tous ceux qui souhaitent porter un programme Windows vers Linux devraient penser à la SDL. La SDL est une bibliothèque C, qui vous permettra de gérer tous les problèmes de fenêtrage et cela de manière indépendante à la plateforme (même pour les mobiles). La SDL est utilisée dans tous les portages de Valve, même dans Steam.

IV-B - Système de fichiers

Le premier point à noter est la sensibilité à la casse de Linux. En effet, Windows ne fait pas de différence entre majuscules et minuscules, mais Linux si. La première solution est de mettre en minuscules tous les noms des ressources et des répertoires. La seconde est de construire un cache de fichiers et de chercher pour les noms similaires.

IV-C - Autres problèmes

IV-C-1 - #define

Valve possédaient quelques #define problématiques, notamment certains programmeurs assimilent Linux à un serveur dédié.

IV-C-2 - Internationalisation

De plus, le studio a rencontré des problèmes liés à l'internationalisation cassant certains printf()/scanf(). Leur solution a été de définir la locale à en_US.utf8 puis de gérer l'internationalisation en interne. Toutefois, chez les utilisateurs n'ayant pas la locale en_US.utf8, un avertissement est affiché.

IV-C-3 - Police de caractères

Valve conseille de se renseigner sur freetype et fontconfig. Toutefois, la taille des polices n'est pas la même que sous Windows.

IV-C-4 - RDTSC

Au lieu d'utiliser RDTSC pour obtenir le temps CPU, le studio conseille d'utiliser clock_gettime(CLOCK_MONOTONIC) à la place.

IV-C-5 - Entrées souris

Le support des entrées utlisateur est normalement bon, mais quelques gestionnaires de fenêtrages capturent aussi le focus clavier faisant que Alt-Tab interrompt les événements provenant de la souris.

IV-C-6 - Gestion des configurations multiécrans

Même si les configurations multiécrans sont moins bien gérées sous les systèmes d'exploitation Linux, SDL le gère pour vous et vous n'en avez donc pas à vous en soucier.

V - Quels sont les outils sous Linux

Valve propose des binaires compatibles avec la majorité des distributions. Le SDK possède tout ce qui est utile pour compiler dans un ensemble d'outils. De plus, des versions de débogage sont disponibles : https://github.com/ValveSoftware/steam-runtime.

V-A - CPU compilation et débogage

Valve utilise :

- GCC (compilateur) ;

- GDB (débogage) ;

- CGDB (débogage) ;

- LDD (équivalent de dumpbin) ;

- NM (informations sur les symboles) ;

- objdump (désassembleur et informations sur les binaires) ;

- readelf (plus de détails sur les binaires) ;

- make.

V-B - Analyse de performance sur CPU

Valve utilise :

- perf (profileur gratuit) ;

- vtune (Outil d'Intel) ;

- Telemetry.

V-B-1 - Telemetry

Telemetry est un outil payant de RAD Game Tools permettant la visualisation des performances. Celui-ci a un très bas surcoût permettant de le laisser actif durant tout le développement. Il permet d'identifier rapidement les images longues à afficher et donne toutes les informations nécessaires sur cette image.

V-B-2 - Résolution de symboles

Linux possède un système magique de résolution des symboles (équivalent à _NT_SYMBOL_PATH) :

- dans ~/.gbdinit :

- définir debug-file-directory /usr/lib/debug:/mnt/symstore/debug

- /mnt/symstore/debug est partagé et les symboles doivent être transférés dessus

Cela ne fonctionne actuellement qu'avec GDB, mais devrait aussi fonctionner avec l'outil « perf » de Google.

VI - Quels sont les points à connaître entre Direct3D et OpenGL

VI-A - Correspondance des versions

DirectX 9

OpenGL 2

Shaders

DirectX 10

OpenGL 3

Bibliothèque orientée flux
Geometry shaders

DirectX 11

OpenGL 4

Tesselation
Compute shaders

VI-B - Portage

L'idée de Valve est de faire une implémentation de Direct3D 9/10/11 en utilisant OpenGL. Pour cela, une nouvelle DLL serait mise en place dans l'application et le code ne connaîtrait pas quelle bibliothèque est utilisée, même pour le code du rendu.

VI-B-1 - Performance

Les performances ont été un point important, mais finalement, cela n'a pas été un problème. La nouvelle pile de Valve est plus rapide que la pile Direct3D d'environ 20 %.

VI-B-2 - Pièces importantes

Les points importants durant cette transition ont été :

- les textures ;

- les tampons de sommets (« vertex buffers ») ;

- les tampons d'index (« index buffers ») ;

- la création du contexte OpenGL ;

- les shaders.

VI-B-3 - Différences

OpenGL possède des données locales aux threads. Ainsi :

- un thread ne peut avoir qu'un seul contexte actif ;

- un contexte ne peut être courant que dans un seul thread à la fois ;

- les appels OpenGL ne font rien lorsqu'il n'y a aucun contexte ;

- MakeCurrent affecte les relations entre le thread courant et le contexte.

OpenGL est une bibliothèque C. Chaque objet possède un identifiant. Beaucoup de fonctions n'utilisent pas d'identifiant et agissent sur les objets courants. Normalement, un identifiant est un GLuint.

OpenGL supporte les extensions alors que Direct3D n'en a officiellement pas. OpenGL est bavard, mais très efficace : ne jugez pas un morceau de code au nombre de lignes, donc profilez.

VI-B-3-a - Objets OpenGL

OpenGL possède de multiples objets : textures, tampons, FBO…

Un objet est défini comme courant à l'aide d'un sélecteur. Les modifications suivantes sont alors appliquées à celui-ci. La plupart des objets possèdent un objet par défaut : 0.

VI-B-3-b - Extensions

Il est conseillé d'utiliser GLEW. Il existe des extensions par plateforme et par constructeur (mais qui peuvent être supportées par d'autres).

VI-B-3-c - Extensions utiles

Les extensions utiles d'après Valve sont :

- EXT_direct_state_access (la plupart des fonctions prennent un nom d'objet directement, sans avoir besoin de liaison. Le code devient plus facile à lire et nécessite moins de transitions. Similaire aux modèles d'utilisation de Direct3D. Sachant que l'extension est implémentée du côté du pilote, vous pouvez la réimplémenter lorsqu'elle n'est pas présente.) ;

- EXT_swap_interval (permet le changement de synchronisation verticale) ;

- EXT_swap_control_tear (permet le tearing lorsque l'image n'est pas prête à temps. Extension demandée par John Carmack et implémentée rapidement par la suite) ;

- ARB_debug_output (gestion d'erreur par callback permettant de parcourir la pile d'appels) ;

- ARB_texture_storage ;

- ARB_sampler_objects.

VI-B-3-d - Autres extensions intéressantes

- NVX_gpu_memory_info/GL_ATI_meminfo (récupère des informations sur le GPU) ;

- GL_GREMEDY_string_marker (équivalent à D3DPERF) ;

- GL_ARB_vertex_array_bgra (correspond mieux aux UINT de D3D) ;

- GL_APPLE_client_storage/GL_APPLE_texture_range (pas pour Linux, mais utile pour Mac).

VI-B-4 - Astuces

Lors d'une recherche Google, cherchez avec et sans GL_.

Lisez les spécifications (même si cela est difficile), cela vous permettra de mieux comprendre le fonctionnement des cartes graphiques et du pipeline de rendu.

VI-C - Lacunes d'OpenGL

Valve a aussi rencontré quelques problèmes avec OpenGL :

- fonctionnels :

- états des textures → peut être résolu avec ARB_sampler_objects,

- différence d'ordre des matrices et sens des textures,

- différences d'origine des textures,

- convention du pixel central (OpenGL correspond à DirectX10) ;

- performance :

- problèmes de MakeCurrent (préférer un seul thread, ne pas tenter de l'appeler à chaque frame) ;

- sérialisation du pilote (les pilotes récents sont multithreads et la synchronisation des données est coûteuse, il faut dont éviter glGet*, glGetError*, les fonctions qui retournent une valeur, les fonctions qui copient un nombre non déterminable de données du client. ARB_debug_output peut vous aider).

De plus, chaque vendeur possède des différences dans l'implémentation et il faut tester au moins une fois pour chacun d'entre eux.

VI-C-1 - Création d'un contexte OpenGL

La création d'un contexte OpenGL est simple. Il suffit de créer une fenêtre, puis le contexte, mais cela ne vous donnera pas nécessairement le contexte spécifié. La création de contexte « robuste » est plus difficile et nécessite des extensions WGL/GLX :

- créer une fenêtre (à ne pas afficher) ;

- créer un contexte ;

- faire une requête pour récupérer les extensions spécifiques aux fenêtres (WGL/GLX) ;

- créer une autre fenêtre (fenêtre de l'application) ;

- créer un contexte en utilisant les fonctions des extensions ;

- détruire le premier contexte ;

- détruire la première fenêtre.

Avec la SDL 2, cela se résume à SDL_GL_SetAttribute et SDL_CreateWindow.

VI-D - Divers

VI-D-1 - Attributs de sommets

Il est nécessaire de relier le tampon avant son utilisation. Si cela vous déplaît, vous pouvez utiliser les Vertex Attribute Objects (VAO), mais cela est plus lent sur toutes les implémentations, donc Valve le déconseille. À la place, l'extension ARB_vertex_attrib_binding pourra vous aider et rend le code plus facile à lire.

VI-D-2 - Mise à jour des tampons

Il est déconseillé d'utiliser MapBuffer qui retourne un pointeur, car cela provoque la sérialisation du pilote ou pire, cela peut provoquer une nécessité de synchronisation entre le CPU et le GPU. À la place, il faut utiliser BufferSubData, ou BufferData lorsque vous voulez supprimer tous les éléments que vous aviez.

VI-D-3 - Rendu dans une texture

Les Frame Buffer Objects (FBO) permettent le rendu dans une texture en OpenGL. Il faut toutefois noter qu'ils ne sont pas partagés entre les contextes. Valve utilise un cache de FBO permettant de toujours récupérer un FBO correspondant à la cible de rendu voulue. Ainsi, les échanges d'attachements sont évités. En effet, le pilote effectue de nombreuses vérifications lors de ces échanges et donc, vous fait perdre du temps.

VI-D-4 - Shaders

Il est nécessaire d'être précautionneux lors d'un changement de shaders, car les variables uniformes définies pour celui-ci sont aussi changés. Une solution est d'utiliser les Uniform Buffer Object (UBO), mais vous pouvez aussi utiliser une trace globale de vos variables uniformes et les redéfinir avant l'affichage.

De plus, Valve utilise une table de hachage pour les program shaders (notamment pour ne pas les traduire du HLSL plusieurs fois).

VI-D-5 - Performances

Les fonctions les plus appelées peuvent être « inline » pour améliorer les performances, mais n'oubliez pas de profiler l'application pour vérifier le gain de cette pratique.

De plus, il est quelques fois nécessaire d'écrire du code spécifique au constructeur de la carte graphique afin de l'optimiser pour celle-ci. Vous avez aussi l'avantage de la spécification publique, vous permettant de vous retourner auprès du constructeur et de lui rapporter des bogues.

VI-D-6 - Outils

Voici une liste d'outils utilisés par Valve :

- NVIDIA Nsight ;

- PerfStudio et gDEBugger ;

- CodeXL ;

- Apitrace.

VII - Remerciements

Merci à ClaudeLELOUP pour sa relecture orthographique.