Fredz's blog

Aller au contenu | Aller au menu | Aller à la recherche

vendredi 18 juin 2010

Genlock, comment ça fonctionne

J'ai parcouru le code et même si je n'ai pas tout compris, ça commence à s'éclairer un peu. Je ne vais parler que de la partie graphique, la gestion du port parallèle ne m'intéresse que dans la mesure où je peux la supprimer, en conservant néanmoins le pilotage des lunettes via ce port.

Programmation par IRQ

Visiblement tout ce qui concerne le page flipping et la synchronisation avec le retour vertical se passe dans la fonction qui est appelée à chaque fois que l'interruption reliée à la carte graphique NVIDIA (IRQ 16 par défaut) est déclenchée.

Il faudra faire en sorte que la valeur de cette interruption soit lue directement et pas définie par défaut, probablement en parsant /proc/driver/nvidia/cards/*. Il faudra voir si on a besoin de prendre en compte plusieurs interruptions s'il y a plusieurs cartes, ce serait quand même bien de gérer le SLI. Ce n'est pas le cas sur l'ancien driver NVIDIA et les drivers iZ3D et Tridef, à voir pour le driver NVIDIA 3D Vision.

Initialisation

Dans un premier temps, cette fonction entre dans un mode d'initialisation dans lequel la durée d'une trame est calculée, en mesurant le temps entre un début/fin de retour vertical et le suivant. La fonction passe ensuite en mode synchronisation.

Synchronisation

En mode synchronisation, il y a une évaluation de la différence entre le temps actuel et le temps évalué précédemment pour le prochain retour vertical. Si cette différence est inférieure à -1000 µs, ce n'est pas un retour vertical et on rend la main de suite. Si cette différence est supérieure à 1000 µs, on a sans doute raté un retour vertical et on l'affiche dans le log. Sinon il y a un code assez compliqué à base de compteurs auquel je n'ai rien compris pour l'instant pour les valeurs entre -1000 et -100 µs et 1000 et 100 µs.

Si on a un retour vertical, raté ou pas, on envoie la séquence de page flipping en programmant le CRTC VGA et on envoie le signal adéquat aux lunettes suivant qu'on est sur un retour vertical pair ou impair. La parité de la valeur du retour n'a de valeur qu'en interne, on ne peut pas s'en servir pour garantir une obturation des lunettes toujours dans le bon ordre après activation du module. Il faudra donc bien inclure un code de pilotage direct via le bit SDA du DDC en s'appuyant sur I2C pour les lunettes VGA DDC.

Pilotage du CRTC

Le pilotage du CRTC semble avoir quelques limites, déjà il faut séparer la détection du retour de la gestion du page flipping.

Page flipping

Pour l'instant il y a du code pour faire du page flipping via les registres standards du CRTC VGA. Les cartes NVIDIA disposant de deux CRTCs pour le TwinView, il faudra trouver les adresses du deuxième CRTC en lisant de la doc ou en étudiant nvclock ou les drivers nv et nouveau.

Ça permettrait de pouvoir au moins choisir lequel des deux écrans utiliser, étant donné que pour l'instant le page flipping s'active toujours sur le même, quelles que soient les options sélectionnées dans nvidia-settings ou la valeur choisie pour la variable head dans le code. Il est possible que les adresses pour le deuxième CRTC (choisi selon la valeur de la variable head) soient erronées dans le code après comparaison avec le code de nv. Mais il va falloir pousser un peu plus loin parce que ce n'est pas programmé du tout pareil.

Il faudra voir également si on peut faire du page flipping sur deux écrans, ce qui semble ne pas être possible en lisant les docs NVIDIA. En effet celles-ci indiquent qu'avec XVideo ou OpenGL on ne peut synchroniser sur le retour vertical que pour un seul écran. Il faudra voir si ça concerne le page flipping, la détection du retour vertical ou les deux.

Ça pourrait concerner le page flipping parce que selon les docs il y aurait un unique framebuffer en TwinView sur lequel les deux CRTCs s'appuient et on ne pourrait donc changer l'adresse que de l'un des deux. Ça me paraît bizarre étant donné que le code actuel ne fait du page flipping que sur un seul écran, l'autre affichant toujours la même chose.

Limite de résolution pour le page flipping

Une seconde limite tient à la résolution supportée pour le page flipping. Pour l'instant, la résolution horizontale est fixée à 1024 dans le code, il faudra essayer avec des modes supérieurs à 1024x768 pour voir si ça fonctionne.

Dans le code de page flipping, il est indiqué que le code supporte du 1024 ou inférieur en offset, donc c'est un peu inquiétant. Il est aussi indiqué qu'auparavant le code gérait des offsets supérieurs à 0x40000 (256K, la mémoire maxi en VGA) mais que ça a été supprimé à cause d'un accès à des registres NVIDIA spécifiques qui créent des instabilités.

Donc je ne sais pas du tout ce qui est supporté concernant les modes graphiques. Je ne comprends d'ailleurs pas comment il définit l'offset en ne se basant que sur le nombre de colonnes et le nombre d'octets par pixel. Si on a un framebuffer avec les données alignées, il faudrait aussi utiliser la résolution verticale pour le calcul. Il va falloir relire ce code pour voir ce que ça fait réellement.

Détection du retour vertical

La détection du retour vertical ne me semble pas être très optimale. En regardant un peu la doc sur la programmation des registres VGA, on voit qu'il existe un registre sur lequel on peut lire l'état du retour pour les cartes EGA (ouais, ça nous rajeunit pas :).

@@ 3C2h (R): Input Status #0 Register

        bit 4  Status of the switch selected by the Miscellaneous Output
               Register 3C2h bit 2-3. Switch high if set.
            5  (EGA Only) Pin 19 of the Feature Connector (FEAT0)
                          is high if set
            6  (EGA Only) Pin 17 of the Feature Connector (FEAT1)
                          is high if set
            7  (EGA Only ??) If set IRQ 2 has happened due to Vertical
                     Retrace. Should be cleared by IRQ 2 interrupt routine
                     by clearing port 3d4h index 11h bit 4.@@

Donc on pourrait peut-être se passer des évaluations de la durée d'une trame et juste regarder l'état du bit 7 de ce registre pour savoir si l'interruption est due à un retour vertical. À tester bien évidemment, voir aussi s'il n'existe pas un registre équivalent pour les cartes VGA ou NVIDIA. Ça semblerait logique, sinon c'est assez goret comme façon de détecter les retours verticaux.

Visiblement il n'y a qu'une IRQ, donc est-ce vraiment possible de détecter les retours pour deux écrans ? S'il y a deux CRTC, je ne vois vraiment pas pourquoi on ne pourrait pas. Après tout, OpenGL et XVidéo arrivent bien à tester ce retour pour l'un ou l'autre des écrans et c'est pris en compte immédiatement. Ça semble indiquer qu'il y a juste lecture d'un flag et pas une reprogrammation hard, donc pourquoi pas...

Calculs d'ajustement

Il y a également quelques calculs qui sont faits dans cette fonction indépendamment de l'état de l'application. Ça concerne probablement des ajustements pour la synchronisation des machines distantes, mais je n'ai pas compris comment ça fonctionnait. Ce sera probablement du code à supprimer, mais il faudrait quand même comprendre à quoi ça sert avant.

jeudi 17 juin 2010

Suppression de l'interruption sur port parallèle dans NVStereo

Pas beaucoup de progrès aujourd'hui, j'ai juste supprimé le code relatif à l'interception de l'IRQ du port parallèle pour le genlocking des machines esclaves. Et encore, je ne suis même pas sûr que mes modifications n'aient pas abimé quelque chose, mais ça a l'air de fonctionner d'après mes tests.

Il faut dire que le code de Genlock est tellement mal écrit et peu (pas ?) documenté que ce n'est pas vraiment une partie de plaisir, les mecs qui ont pondu ça programment comme des gorets. Déjà, un unique fichier C de 1690 lignes, ça aurait dû me mettre la puce à l'oreille...

Du coup, je me demande si je ne vais pas repartir de zéro, parce que faire du refactoring sur un code aussi pourri ne m'enchante pas particulièrement. C'est vraiment dommage parce qu'il y a plein de fonctionnalités vraiment géniales là dedans et que le code fonctionne, étonnamment...

mardi 15 juin 2010

NVStereo, un module basé sur Genlock

Suite à mes tests avec Genlock, j'envisage d'écrire un nouveau module basé sur ce dernier qui s'appellera NVStereo. NV parce qu'il ne concernera dans un premier temps que les cartes graphiques NVIDIA et Stereo parce que je compte supprimer les fonctions de genlocking/framelocking et ne conserver que le code relatif à la stéréo en page flipping sur un unique écran.

Voici les modifications que je compte inclure dans NVStereo par ordre de priorité :

  1. suppression du code relatif au genlocking/framelocking
  2. suppression de la gestion du port parallèle pour le genlocking et le pilotage de lunettes
  3. inversion du rendu quand un retour vertical est manqué
  4. inversion du rendu par une application en user space en temps réel
  5. activation/désactivation du page flipping par une application en user space en temps réel
  6. détection de la parité des trames pour toujours alterner les trames dans un ordre prédéfini
  7. détection des changements de mode graphique et recalcul de la durée d'une trame
  8. support des lunettes LCD à obturation VGA DDC
  9. choix du CRTC pour la synchronisation avec le retour vertical
  10. support des lunettes GeForce 3D Vision si je trouve un moyen de tester les bouts de code existants

À terme, j'aimerais m'en servir comme base pour un futur driver 3D généraliste capable de fonctionner aussi bien avec OpenGL qu'avec XVideo et peut-être X Window de façon plus générale. Il faudra également trouver de la doc pour adapter le code à d'autres marques de cartes graphiques (ATI et Intel dans un premier temps) et procéder à quelques adaptations supplémentaires.

Concernant les cartes NVIDIA, le code de détection du retour vertical ne devrait pas avoir à subir de changements, mais il faudra essayer de faire en sorte que le page flipping puisse fonctionner sans écran virtuel et si possible en mode fenêtré. Pour X Window et/ou XVideo il faudrait tester avec un Pixmap en mémoire vidéo, qu'il faudra sans doute marquer d'un tag spécial pour que le driver sache de quel Pixmap il doit s'occuper. Il sera sans doute impossible de gérer autre chose que des modes plein écran avec le page flipping.

À priori le driver stéréo 3D NVIDIA fonctionne d'une manière similaire pour l'affichage de vidéos et d'images, en utilisant une surface vidéo avec une résolution horizontale double et une ligne supplémentaire contenant un tag spécifique ainsi que des paramètres pour le rendu.

Les paramètres sont à priori les suivants pour le tag :

  • unsigned int dwSignature;
  • unsigned int dwWidth; // à priori non utilisé pour l'instant (trouvé sur MTBS3D)
  • unsigned int dwHeight; // à priori non utilisé pour l'instant (trouvé sur MTBS3D)
  • unsigned int dwBPP;
  • unsigned int dwFlags;

Contenu de la signature :

  • #define NVSTEREO_IMAGE_SIGNATURE 0x4433564e //NV3D

Et pour les flags :

  • #define SIH_SWAP_EYES 0x00000001
  • #define SIH_SCALE_TO_FIT 0x00000002
  • #define SIH_SCALE_TO_FIT2 0x00000004 // à utiliser avec le précédent pour conserver l'aspect ratio (trouvé sur MTBS3D)

Ensuite cette surface est envoyée vers le back buffer avec un StretchRect et la vidéo est affichée en 3D lorsque le back buffer est présenté à l'écran. Il faudra voir s'il y a besoin d'utiliser un back buffer avec X Window/XVideo (éventuellement avec DBE) ou si on peut se contenter de pixmaps en mémoire vidéo.

Il faudra voir si la même chose est possible sous OpenGL avec des FBOs ou trouver une autre technique en étudiant le mode de fonctionnement des drivers stéréo NVIDIA sous Direct3D et/ou OpenGL.

Il faudra également ajouter du code pour la gestion de divers paramètres, comme la séparation et la convergence par exemple.

Genlock fonctionne à 120 Hz !

En regardant le code d'un peu plus près, j'ai remarqué qu'il y avait une fonction d'initialisation chargée d'évaluer la durée d'une trame entre deux retours verticaux. C'est pour cette raison que Genlock ne fonctionnait qu'à 60 Hz sur ma machine, cette fonction n'était jamais appelée lorsque le module restait chargé en mémoire pendant un changement de fréquence.

Il suffisait donc de décharger le module avant de changer la fréquence du moniteur puis de le recharger pour que ça fonctionne pour la nouvelle fréquence. J'ai testé en choisissant un mode graphique à 120 Hz sur mon moniteur CRT et ça fonctionne correctement.

Le driver rate rarement un retour vertical et si ça arrive, il le détecte correctement de toute façon en affichant un message dans syslog. Un autre avantage de cette implémentation est qu'elle n'a quasiment aucun impact sur les performances du système, ce que j'ai constaté avec un simple « top ».

En effet, la synchronisation avec le retour vertical se fait via une interruption matérielle (IRQ), il n'y a donc pas de boucle d'attente qui pourrait être gourmande en cycles CPU. D'autre part le page flipping est matériel, il ne nécessite donc pas une longue opération de copie d'un buffer vers l'écran. Il consiste uniquement à reprogrammer le CRTC pour le faire pointer vers la bonne partie de l'écran à afficher, ce qui prend un temps négligeable.

Il reste encore pas mal de boulot pour faire en sorte que ce système fonctionne sans écran virtuel et pour le rendre compatible avec OpenGL (pour les jeux et applications 3D) et X Video (pour la lecture de vidéos 3D), mais c'est un bon début. Il faudra également lui ajouter le support du signal DDC VGA pour qu'ils puisse fonctionner avec toutes les lunettes de ce type et pas seulement les eDimensional avec l'activator.

Pour l'instant la compatibilité avec la gamme complète des cartes graphiques NVIDIA reste à tester, mais comme les tests des auteurs de Genlock l'ont utilisé avec des cartes type GeForce 4 et que ça fonctione sur ma GeForce 7600 GT, j'ai bon espoir pour le reste de la gamme. Ce sera de toute façon limité au matériel NVIDIA dans un premier temps, mais il suffirait de trouver des informations techniques pour d'autres matériels concernant la gestion du retour vertical par IRQ et le page flipping matériel pour étendre sa compatibilité.

En tout cas, ça ouvre de grands espoirs de voir enfin apparaître rapidement une solution pour la stéréoscopie 3D sous Linux.

Compilation et utilisation de Genlock sous Linux

J'ai finalement réussi à faire quelque chose de Genlock. J'ai écrit de la doc sur sa compilation et son utilisation sur les forums de MTBS3D. Si j'ai le courage, je traduirai tout ça en français à l'occasion.

Le lien vers le forum : Compiling and using Genlock on Linux

En gros, ça fonctionne, il suffit de créer un écran virtuel avec une largeur d'écran double et d'activer le mode stereo de Genlock. Dans ce cas, les parties gauche et droite de l'écran sont affichées successivement en synchronisation avec le retour vertical. En utilisant des lunettes eDimensional avec l'activator du fournisseur sous Wine, on peut voir la partie gauche avec un oeil et la partie droite avec l'autre.

Le problème c'est que ça ne fonctionne qu'avec une fréquence de rafraîchissement vertical de 60 Hz (en DoubleScan) sur ma machine. Sinon l'affichage n'est pas correctement synchronisé et Genlock génère des messages d'erreur dans syslog, indiquant qu'on est probablement pas synchronisé et qu'il rate des trames.

Adaptation de « Genlock » pour les noyaux récents

Dans ma recherche d'une solution pour obtenir une synchronisation de l'affichage avec le retour vertical je me suis penché sur l'application Genlock (dérivée de SoftGenLock).

En faisant quelques modifications, j'ai réussi à faire en sorte qu'elle compile sur ma machine (noyau 2.6.32) et j'arrive à charger le module genlock et à communiquer avec lui (lecture de l'état, envoi de commandes).

Par contre ça n'a pas l'air de produire quoi que ce soit de visible, il va falloir que je me penche sérieusement sur le code pour voir ce que ça peut faire étant donné que la documentation est assez spartiate...

dimanche 13 juin 2010

Déception sur l'utilisation d'un thread

J'ai fait un test pour mettre le code d'affichage dans un thread, mais ce n'est pas du tout concluant. Je n'arrive pas à obtenir un rendu des images synchronisé avec le balayage de l'écran avec cette technique. De toute façon ça ne semblait pas être une très bonne solution de faire tout ça en espace utilisateur, il y aurait forcément eu des problèmes de synchro vu le type de scheduling du noyau (pas temps réel).

SoftGenLock/GenLock

Il va donc falloir trouver une autre solution pour synchroniser l'affichage de chaque image avec le retour vertical de l'écran. La solution qui me semble être la plus logique serait de faire comme dans Genlock (basé sur SofGenLock), écrire un module pour le noyau Linux (2.6) qui s'occupe de la synchronisation. Il va falloir rentrer dans leur code pour voir comment ils font et déterminer si c'est faisable.

Rendu YV12 off-screen

Il faudra également trouver une meilleure solution pour le rendu des XVimage vers des pixmaps. Pour l'instant, je fais le rendu vers une fenêtre et je copie le résultat vers un Pixmap, mais si cette fenêtre est visible il faut afficher l'image qui correspond au bon oeil, ce qui me semble compliqué à faire. Il faudra donc essayer de faire le rendu vers une partie non visible de l'écran, peut-être une fenêtre non mappée ou une fenêtre avec backing-store. L'idéal étant d'avoir deux fenêtres non visibles, dont on copie le contenu alternativement vers la fenêtre visible, une sorte de double buffer à trois fenêtres...