# Thursday, August 20, 2009

Encore une idée que je ne ferais pas : Linq To HLSL

C’est en lisant ce tweet de Robert Pickering que j’ai eu l’idée d’écrire des posts sur les différentes idées (bizarre, originales ou farfelues) qu’il m’arrive d’avoir. En effet, comme l’indique Robert, et comme beaucoup d’entre vous je suppose, il m’arrive parfois d’avoir des idées qui m’intéressent et qui me motivent mais par soucis de temps ou de compétences, je ne les réalise que rarement. Je me propose donc de partager dorénavant certaines de ces idées avec vous. Le but étant bien entendu d’avoir vos réactions, commentaires et remarques. Commençons donc dès à présent avec la dernière idée en date que j’ai eu (début de cette semaine).

Les GPU ont actuellement une puissance de calcul supérieure aux CPU et malheureusement il n’est pas simple d’en profiter. Microsoft a étudié cette faisabilité via le projet Accelerator initié par Microsoft Research. DirectX 11 poursuit dans cette voie grâce à l’apparition des Compute Shaders. Ces shaders particuliers n’ont pas pour objectif de modifier un rendu graphique (d’effectuer des traitements sur des pixels) mais d’effectuer différents types de calculs.

L’idée d’exploiter ces shaders m’est venu en rentrant un peu plus en détail dans le Windows API Code Pack for .net. Ce projet disponible sur Code Gallery permet principalement d’exploiter les nouvelles APIs apparues avec Windows 7 directement depuis du code managé. Mais l’API Code Pack for .net propose d’autres fonctionnalités tels que des wrappers managés de DirectX 10.x et DirectX 11. Et en consultant la documentation de ces wrappers je me suis rendu compte qu’il était possible d’appeler et d’exécuter des Compute Shaders directement depuis du code managé. Ce fut une surprise puisque depuis DirectX 9 qui disposait d’une version managée (un wrapper intelligent nommé Managed DirectX), Microsoft avait fait un retour en arrière en ne proposant de développer des applications DirectX qu’en code natif. Fonçant tête la première et pompant allègrement un code natif trouvé sur le net je me retrouve rapidement avec ces quelques lignes de code :

SwapChain swapChain;
SwapChainDescription swapChainDescription = new SwapChainDescription();
swapChainDescription.BufferCount = 1;
swapChainDescription.BufferDescription.Width = 1024;
swapChainDescription.BufferDescription.Height = 768;
swapChainDescription.BufferDescription.Format = Format.R8G8B8A8_UNORM;
swapChainDescription.BufferDescription.RefreshRate.Numerator = 60;
swapChainDescription.BufferDescription.RefreshRate.Denominator = 1;
swapChainDescription.BufferUsage = UsageOption.RenderTargetOutput;
swapChainDescription.OutputWindowHandle = m_windowHandle;
swapChainDescription.SampleDescription.Count = 1;
swapChainDescription.SampleDescription.Quality = 0;
swapChainDescription.Windowed = true;
var levels = new FeatureLevel[]
                            {
                                FeatureLevel.FeatureLevel_11_0,
                                FeatureLevel.FeatureLevel_10_1,
                                FeatureLevel.FeatureLevel_10_0
                            };
var driverTypes = new DriverType[]
                      {
                          DriverType.Hardware,
                          DriverType.Reference
                      };
D3DDevice device = null;

foreach(var driverType in driverTypes)
{
     device = D3DDevice.CreateDeviceAndSwapChain(null, driverType, null,CreateDeviceFlag.Default , levels,
                                                          swapChainDescription, out swapChain);
}

//Check Support for Compute Shader
FeatureDataD3D10XHardwareOptions options;
device.CheckFeatureDataD3D10XHardwareOptions(out options);
if(!options.ComputeShadersPlusRawAndStructuredBuffersViaShader4x)
    return;

device.CreateComputeShader();

Comme vous le devinez, la méthode CreateComputeShader attend un paramètre afin de pouvoir être exécutée. Il faut en effet lui passer un pointeur vers le code d’un Compute Shader compilé. Le problème suivant auquel j’ai donc été confronté fut donc de développer un compute shader. Comme les shaders traditionnels, ceux-ci se développent dans un langage spécialisé : le HLSL. Et après avoir jeté un oeil dans la documentation de ce langage, je me suis vite rendu compte qu’il n’allait pas être simple d’implémenter un traitement utile avec ce langage basé sur une syntaxe proche du C. Le langage propose en effet un ensemble de types assez restreint et ne permet pas au pauvre développeur managé que je suis de faire ce que je souhaite de manière simple. Partant de ce constat, et pensant qu’il serait utile de simplifier l’accès au GPU via ces compute shader, j’ai donc songé à l’idée de créer un provider Linq To HLSL. L’objectif de ce provider est “simple” : Générer du code HLSL à partir d’un arbre d’expression, le compiler en mémoire (ah mince D3DCompile ne semble pas implementé dans l’API Code Pack for .net), l’exécuter et récupérer les informations en mémoire. Bien sûr en pratique, il existe plusieurs difficultés de taille, notamment celle concernant le nombre de types restreints supportés par HLSL contrairement aux langages managés. Il faudrait lors de la génération de code convertir les types non supportés (quand cela est possible) vers des types HLSL. Une chaine de caractères devrait être ainsi par exemple converti en tableau d’entiers avant de pouvoir être manipulé.

Pourquoi je ne le ferais pas :

  • Il n’existe pas encore de cartes graphiques compatible DirectX 11. La première qui sera disponible sera une carte AMD et devrait être mise en vente le 10 septembre. Il n’est donc pas encore possible de tester les réelles performances du projet.
  • Cela m’obligerait à apprendre un nouveau langage HLSL, langage très spécifique qu’il peu probable que j’utilise dans le futur.
  • Je n’ai absolument aucune idée du temps nécessaire pour développer ce type de provider (je sais que ça se rapproche de beaucoup mais je ne suis pas capable d’être plus précis que cela).
  • Et enfin, il semblerait que le langage Axum supporte à la fois le multi-core (plusieurs coeurs/processeurs) et many-core (différents types de coeurs (CPU/GPU)). Etudier ce projet devrait donc être plus pertinent que de développer ce provider.