Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Silverlight Underground

Retrogaming, Casual Games, Emulation with Silverlight
SilverStone, ou la pierre philosophale de Silverlight

Comme vous avez pu le constater, l'activité s'est quelque peu réduite sur mon blog ces derniers temps. Malheureusement, il n'est pas toujours facile de jongler entre vie professionnelle, vie extra-professionnelle et vie de famille... Tongue Tied

Je tenais donc à vous faire partager mes réflexions sur un nouveau projet Silverlight sur lequel je compte travailler pendant mon temps libre : "SilverStone". Je pense qu'il est prometteur et surtout qu'il ouvrira de nouveaux horizons à Silverlight.

Tout d'abord, un petit historique s'impose. Il faut savoir qu'avant de m'intéresser à cette technologie Microsoft, je travaillais essentiellement sur des émulateurs et des portages de jeux vidéos avec le language Java. C'est en juillet de l'année dernière, que j'ai découvert une vidéo impressionnante de Quake en Flash (http://www.youtube.com/watch?v=0hX-Uh3oTcE). Il s'agissait en fait d'une preview d'un projet de recherche d'Abobe, Adobe Alchemy. Cette démonstration technique a d'ailleurs fortement inspiré mon portage de Quake en Silverlight.

Adobe Alchemy (http://labs.adobe.com/technologies/alchemy) est un outil qui se présente sous la forme d'un environnement de compilation pour Flash 10. Il permet de convertir n'importe quelle application écrite en language C en du bytecode Flash (AVM2). Le bytecode Flash généré s'exécute ensuite dynamiquement sur la machine virtuelle de Flash. Alchemy utilise en fait en interne le célèbre projet open-source LLVM (http://www.llvm.org/). LLVM permet de transformer différents languages de haut niveau comme le C ou le Fortran dans un unique language générique de bas niveau, le bytecode LLVM. Il est possible d'appliquer au code LLVM des optimisations très performantes, voire meilleures que celles que proposerait GCC. Pour arriver au résultat final, Adobe a développé un backend particulier qui transforme ce code LLVM en bytecode Flash.

Quake a été porté avec cette technique, et plus récemment on a vu fleurir d'autres projets comme une librairie Ogg Vorbis ou d'autres jeux comme Doom, Heretic ou Hexen (http://www.newgrounds.com/portal/view/470460). Bluffant...

Pourrions-nous appliquer cette même chaîne de compilation à Silverlight ? En théorie oui. Partant du code générique LLVM, on pourrait générer du bytecode MSIL pour Silverlight. La tâche s'annonce malgré tout ardue...

Depuis l'année dernière, j'ai donc exploré plusieurs pistes pour porter des programmes écrits en language C (comme Quake) en C# et Silverlight.

 

Portage "manuel"

Pour arriver à porter le jeu Quake en Silverlight, j'ai choisi un portage classique. J'ai repris chaque module du programme au fur et à mesure et je les ai converti en classes C#. Pour faciliter la lecture et le débogage du portage, j'ai choisi de conserver le maximum du code originel.

Pour garder la hiérarchie de fichiers sources (.h et .c), j'ai utilisé un seul namespace commun et des classes partielles pour les modules. L'ensemble des variables et des fonctions sont devenues statiques et publiques pour la plupart.

/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

// client.h

namespace quake
{

    public partial class client

La majeure partie du code a été recopié tel quel, il suffisait parfois de remplacer certaines syntaxes purement C par du code C# plus classique (pas de -> par exemple).

Les premières difficultés sont apparues avec le portage des fonctions de la librairie standard du C. J'ai donc créé un wrapper en language C# et Silverlight pour gérer les entrées/sorties, certaines opérations mémoires, etc.

public static void fseek(FILE file, int position, int seek)
{
    file.stream.Seek(position, SeekOrigin.Begin);
}

public static void fclose(FILE file)
{
    file.stream.Close();
    file.stream = null;
    file = null;
}

Plus difficile a été la conversion des opérations sur les pointeurs. Silverlight tournant dans une sandbox, pas question d'écrire du code unsafe et donc d'utiliser des pointeurs. A cet effet, j'ai défini des classes très utiles pour simuler les pointeurs sur des buffers mémoire.

#region ByteBuffer
public class ByteBuffer
{
    public byte[] buffer;
    public int ofs;

    public ByteBuffer(byte[] buffer)
     : this(buffer, 0)
    { }
    public ByteBuffer(ByteBuffer buf)
     : this(buf.buffer, buf.ofs)
    { }
    public ByteBuffer(ByteBuffer buf, int ofs)
     : this(buf.buffer, buf.ofs + ofs)
    { }
    public ByteBuffer(byte[] buffer, int ofs)
    {
        this.buffer = buffer;
        this.ofs = ofs;
    }

    public byte this[int index]
    {
        get { return buffer[ofs + index]; }
        set { buffer[ofs + index] = value; }
    }

    public static ByteBuffer operator +(ByteBuffer obj, int ofs)
    {
        return new ByteBuffer(obj.buffer, obj.ofs + ofs);
    }

    public void Add(int ofs)
    {
        this.ofs += ofs;
    }
    public void Sub(int ofs)
    {
        this.ofs -= ofs;
    }

    public static bool operator >=(ByteBuffer obj1, ByteBuffer obj2)
    {
        return obj1.ofs >= obj2.ofs;
    }

    public static bool operator <=(ByteBuffer obj1, ByteBuffer obj2)
    {
        return obj1.ofs <= obj2.ofs;
    }
}
#endregion

Au final, avec cette technique, longue et fastidieuse, j'ai pu porter la plupart du code source de Quake et y apporter certaines modifications. Les plus gros problèmes rencontrés sont les bugs liés au portage. Quand on passe d'un code C avec des structures et des pointeurs à du code C# avec des classes et des références, il est très facile de se tromper et d'avoir des résulats très inattendus (dans mon cas, le décor qui s'autodétruisait au fur et à mesure...). A ce jour, je n'ai toujours pas identifié certains bugs qui empêchent de faire fonctionner le moteur de jeu proprement dit.

 

Portage "assisté"

Parallèlement au développement de Quake en Silverlight et fort d'une demande massive des internautes pour jouer à Quake 2 en Silverlight, j'ai décidé d'expérimenter une autre technique. Le code source de Quake 2 est mieux structuré et me semblait plus adapté à une conversion automatique.

J'ai donc développé un programme en Java (sic) qui parse les fichiers .h et quelques .c et les convertit en fichiers sources C#. Ce générateur de code source conserve les commentaires, met en forme le code C#, convertit les #define en constantes, convertit les types C et les typedefs en types C#, etc.

Plus récemment, j'ai été rejoint par un développeur anglais qui a écrit une version en WPF d'un convertisseur similaire.  Au final, c'est un outil qui facilite le portage et permet de diminuer le travail répétif du portage "manuel".

Bien entendu, le résultat ne peut être parfait. Il faudrait pouvoir interpréter le code C pour générer du code C# très précis. Par exemple, en fonction du contexte, un char* peut être considéré comme une chaîne de caractères à convertir en String ou bien comme un buffer à convertir en byte[].

Avec cette technique, la plupart des fichiers d'entête de Quake 2 ont été portés en C#. Cela représente une bonne base de départ pour un portage "manuel".

 

Emulation du code natif

Si la conversion de haut niveau ne permet pas un résultat direct, pourquoi ne pas tenter une approche bas niveau ?

Prenons la version de Quake publiée sous MsDos en 1996. Il s'agit d'un programme écrit en language C avec des parties optimisées spécifiques en assembleur. Quake fonctionne en mode protégé à l'aide d'un DOS extender (CWSDPMI.EXE).

Autrement dit, si on considère un logiciel écrit en C#/Silverlight qui émulerait les fonctions de base du couple PC/MsDos et pourrait interpréter du code Intel 80x386 et le mode protégé, il serait possible d'émuler Quake dans cette machine virtuelle.

Je me suis donc attelé à la réalisation d'un tel émulateur et je suis arrivé assez loin dans son développement :

- émulation du processeur 80x86 et 80x386.

- émulation du co-processeur mathématique 80x387 (pour la 3D et les calculs flottants).

- émulation basique des interruptions systèmes (vidéo, MsDos, clavier).

- gestion de fichiers.

- émulation des modes graphiques et textes de la carte vidéo (CGA, VGA).

- support du clavier.

Plusieurs applications, démos et vieux jeux vidéo pour PC (Pacman, Zaxxon) fonctionnent déjà très bien sur cet émulateur. Malheureusement, il reste beaucoup de corrections de bugs mais aussi encore beaucoup de fonctionnalités à émuler (le mode protégé par exemple) avant d'arriver à faire tourner un programme comme Quake.

Se pose aussi la question des performances. Pour émuler le code machine, j'utilise un interpréteur. C'est à dire que chaque opcode est interprété par du code C# équivalent. Pour un fonctionnement beaucoup plus performant, il faudrait pouvoir compiler dynamiquement du code C# à partir du code machine d'origine. Dans tous les cas, les performances ne vaudront jamais celles du portage "manuel" ou "assisté".

 

"SilverStone"

Comme on l'a vu précédemment, une conversion de haut niveau reste très complexe à mettre en oeuvre et ne peut pas substituer un portage "manuel" dans son intégralité. En revanche, une conversion de bas niveau offre une liberté incroyable mais demande des efforts de développement considérables et pas de garantie de performances.

Ce qu'il faudrait en fait, c'est un peu des deux : une conversion de haut niveau avec certaines parties de bas niveau. C'est l'idée maîtresse de SilverStone. Pour transformer le plomb en or, ici le language C en language C#, on pourrait utiliser un outil de conversion qui reposerait sur une machine virtuelle dédiée.

Plus précisément, d'un côté, on a tout ce qui peut se porter facilement d'un language à l'autre : les modules, les commentaires, les constantes, les types de base, etc. De l'autre côté, on a tout ce qui est spécifique au language C, les pointeurs, les structures complexes, les unions, les fonctions de la librairie standard.

L'outil de conversion parse le code source C initial et le convertit en language C#. Tout ce qui concerne les pointeurs et autres subtilités du language C est émulé par une gestion de bas niveau, concrètement une gestion mémoire.

Voici l'architecture de SilverStone telle que je la conçois :

- Un préprocesseur de code source C qui déroule les macros et en option les #define en constantes.

- Un parseur de code source C qui construit un arbre syntaxique complet.

- Un générateur de code source C# qui utilise l'arbre syntaxique précédent. Tout ce qui est spécifique au language C est converti pour être utilisé avec la machine virtuelle : les pointeurs deviennent des entiers, les accès mémoires deviennent des méthodes readmem et writemem, les structures et les tableaux sont alloués et tous les offsets (&x.a[2][3]) et les tailles (sizeof(struct vector_3d)) sont calculés et remplacés dans le code, etc.

- Une machine virtuelle en language C# destiné à faire fonctionner des programmes convertis en Silverlight. Il y a une implémentation de la libraire C standard, la gestion mémoire (heap et stack, les fonctions readmem et writemem) pour stocker les pointeurs, les structures et les données et bien sûr le noyau Silverlight (affichage, synchronisation des threads).

- Un host qui permet d'émuler ou de simuler les fonctions de base de l'environnement sur lequel a été développé le programme en C : un shell, une gestion de fichiers pour Linux ou MsDos, une interface graphique pour Windows, etc.

Il sera donc possible en théorie de convertir de manière automatique et sans intervention manuelle ou presque (l'utilisation de makefiles pour compiler les modules qui vont ensemble) n'importe quel programme en C, pourvu qu'il respecte le language C standard (pas de code _asm inline par exemple) et qu'il n'utilise pas des fonctions systèmes non supportés.

L'idée de pouvoir convertir ainsi des applications, des librairies, des jeux écrits à l'origine en language C en C#/Silverlight me paraît assez séduisante... De plus rien n'empêchera d'utiliser ces programmes avec d'autres programmes écrits en Silverlight mais aussi de les modifier à loisir une fois convertis en C#.

Bien entendu, je vous tiendrai informés sur le blog de toutes les avancées techniques sur ce projet ainsi que des résultats obtenus. A bientôt pour un portage 100% automatique de Quake en Silverlight Big Smile

Astuce : la rétro-compatibilité avec Silverlight

[Introduction]

Voilà c'est fait, vous avez fini par craquer. Vous avez décidé d'installer les outils de développement de Silverlight 3 Beta.

Direction http://silverlight.net/getstarted/silverlight3/default.aspx.

Un premier message de mise en garde retient votre attention à peine quelques instants.

This is a developer beta release only! This means there is no “go-live” licensing available and the end-user runtime of Silverlight 3 is not available. The tools below are intended for software developers only.

Ouais OK, le commun des mortels attendra un peu mais moi je vais jouer avec dès maintenant ! [Rire démoniaque]

Once you install the Silverlight 3 Beta Tools for Visual Studio, your development environment will be a Silverlight 3 Beta environment. Visual Studio 2008 SP1 does not support multi-targeting for Silverlight applications so you will be unable to develop Silverlight 2 applications once these tools are installed. We recommend that you install the Silverlight 3 Beta tools on a separate environment if you still need to have the ability to develop Silverlight 2 applications.

Blablabla... On s'en fiche de tout ça !
Bye bye Silverlight 2, je veux mon SDK tout chaud...
A moi les projections 3D, la génération d'images dynamiques, le raw pipeline pour le son et la vidéo... vers l'infini et au delà !

Bon maintenant que c'est installé, on va s'assurer que ma killer application en Silverlight 2 fonctionne encore.

Voilà, je clique sur halo.sln.
Ah tiens... Le Visual Studio Conversion Wizard ?
Bon bah... Next, Next et Finish...

Cool, tout s'est ouvert normalement. Je lance ma compile... [Suspens insoutenable]

"No errors, no warnings". Quel star de codeur je suis !

Je génère la page HTML maintenant...
Et hop, voilà nickel, ça marche encore. Quels stars ces codeurs de la Silverlight Team !

C'est clair les applications développées en Silverlight 2 sont toujours compatibles dans Silverlight 3 !
(et sinon je peux toujours aller faire un tour ici : http://silverlight.net/forums/51.aspx)

Bon, je vais pouvoir reprendre le développement de Halo en Silverlight 3 cette fois. C'est parti !!!

...

[Quelques semaines de développement plus tard]

...

"Hé, tu le postes quand ton jeu en Silverlight ?"

Vous venez d'être subitement interrompu dans votre frénésie créatrice par votre ami JP sur Live Messenger.
C'est toujours pareil, vous vous éclatez avec ce nouveau SDK et il faut encore livrer des versions avec Silverlight 2. Pfff...

Bon, rien de grave, mon ancien code compilait et fonctionnait encore. Allez hop je publie tout ça sur mon site Web...

"Voilà JP, c'est dispo sur http://www.halosilverlight.com."

"Euh, il me demande d'installer un nouveau runtime mais je l'ai déjà installé Silverlight ?"

"Hum..."

Le souvenir de certaines mises en garde ressurgit soudain dans votre esprit. Le doute vous assaille...
Et si vous ne pouviez plus publier d'applications en Silverlight 2 ?

Noooonnnnn.... Ils avaient raison sur le site officiel ! C'est trop tard, il va falloir désinstaller et réinstaller l'ancienne version !
J'aurai dû lire attentivement. Qué stupido!

...

Et là, la solution finit par arriver là où on ne l'attendait pas...

"Tu as changé quelque chose ?"

Euréka ! Merci JP...
Tout simple, on va comparer la génération Silverlight 2 avec la génération Silverlight 3 et la rendre rétro-compatible !

"JP, tu es mon dieu !"

"... Ah bon ?... C'est par rapport à ta défaite au poker hier soir que tu dis ça ?"

"Non... Non... Bouge pas..."

Voilà, vous sortez l'artillerie lourde, un joli comparateur de fichiers et vous regardez les différences sur la page HTML générée.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
   <param name="source" value="Halo.xap"/>
   <param name="onerror" value="onSilverlightError" />
   <param name="background" value="black" />
   <param name="minRuntimeVersion" value="3.0.40307.0" />
   <param name="autoUpgrade" value="true" />

Ah ouais, bah voilà... le minRuntimeVersion.
Je le modifie de 3.0.40307.0 à 2.0.30923.0 et on n'en parle plus...

"C'est bon tu peux faire F5 !"

"Euh... C'est pareil, vieux."

"Gheee..."

"Tu peux pas me refiler ton ancien XAP, il marchait bien lui ?"

Le fichier XAP... Mais oui bien sûr !

"JP, tu es le dieu des dieux !"

"Arrête... Je vais rougir..."

Un petit coup de WinZip et vous comparez le contenu du XAP.

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="Halo" EntryPointType="Halo.App" RuntimeVersion="3.0.40307.0">
<Deployment.Parts>
<AssemblyPart x:Name="Halo" Source="Halo.dll" />
<AssemblyPart x:Name="SharpZipLib" Source="SharpZipLib.dll" />
</Deployment.Parts>
</Deployment>

Et là, surprise ! En plein dans le AppManifest.xml, le RuntimeVersion...
Encore une fois, je le modifie de 3.0.40307.0 à 2.0.30923.0. Je rezippe le tout et zou !

"Re... C'est bon JP !"

"Ah oui ça y est ça marche... Par contre c'est toujours en 2D ton Halo c'est normal ?"

"Gheee..."

THE END

Un état de l'art des émulateurs en Silverlight

Après plusieurs années passées à réaliser des projets et écrire des émulateurs avec le langage Java, je suis passé sur Silverlight. Les performances entre Java et C#/.NET ne sont pas comparables mais expliquent pourquoi j'ai fini par abandonner le premier.

Développer un émulateur de console de jeux, d'ordinateur ou de machine arcade requiert deux fonctionnalités essentielles : un affichage bitmap rapide et une gestion sonore dynamique.

Premier hic, Silverlight 2 ne permet ni l'un ni l'autre. Bien entendu, il y a toujours des solutions de contournement.

Pour l'affichage, certains ont utilisé des rectangles pour représenter les pixels de l'écran. D'autres ont expérimenté de l'encodage en temps réel d'images PNG. J'ai moi-même utilisé le PNG wrapper que j'ai développé à l'origine pour Quake en Silverlight afin obtenir les meilleurs performances possibles.

Pour la gestion sonore, cela se complique aussi. On peut déjà oublier un éventuel encodage MP3 ou WMA en temps réel. Il n'y pas le choix ici, il faut pré-enregistrer les sons et musiques à émuler au format MP3 ou WMA. En fonction de la machine que l'on cherche à virtualiser, cela peut devenir assez complexe. En effet, il faut pouvoir associer un fichier déjà pré-enregistré à un évènement du CPU, du chipset sonore, etc.

Silverlight 3 change la donne puisqu'il offre de nouveaux espoirs aux développeurs. En terme d'affichage, avec l'API WriteableBitmap et l'accélération matérielle GPU, des performances accrues. Avec le RAW pipeline, la possibilité de gérer de nouveaux formats vidéo ou audio, et aussi de pouvoir faire sa propre génération sonore.

Ceci dit, il reste encore un point important qui n'a pas encore été traité. Ecrire un émulateur, c'est d'abord reproduire logiciellement le fonctionnement d'un processeur qui peut parfois être complexe. En général, on développe un interpréteur des opcodes de ce CPU. Pour émuler un Z80, cela peut paraître suffisant. Mais pour émuler disons un processeur R4000, il faut envisager d'autres techniques comme la recompilation dynamique de code.

En Java, on compile le code émulé en bytecode Java qui sera lui-même exécuté par le Class Loader. En C#, on peut imaginer de générer du code MSIL dynamiquement et le faire exécuter dans l'assembly. Mais avec Silverlight, on est dans une sandbox qui ne permettra pas ce genre de manipulations. Alors, possible ou pas ?

En conclusion, voici un petit état de l'art des émulateurs en Silverlight 2 et 3 :

Emulateur Chip8 en Silverlight
http://www.nokola.com/chipgr8/
Un émulateur Chip8 développé par Nokola.

Emulateur ZX Spectrum en Silverlight
http://www.voxpeeps.com/silverlightspectrumemulator/
Un émulateur ZX Spectrum développé par Chris Hay.

Emulateur NES en Silverlight
http://hrup.dk/blogstuff/neslight2/neslight.html
Un émulateur Nintendo Entertainment System (NES) développé par Søren Alsbjerg Hørup.

Emulateur Sega Master System en Silverlight
http://www.benryves.com/bin/cogwheel/web/cogwheel.php
Un émulateur Sega Master System (SMS) développé par Ben Ryves.

Emulateur C64 en Silverlight
http://silverlightc64.codeplex.com/
Un émulateur Commodore 64 (C64) développé par Pete Brown.

Emulateur PC en Silverlight
http://chenwes.myweb.hinet.net/silpc/
Un émulateur PC/Ms-Dos développé par Chen Wes.

Et je termine bien sûr avec mes propres projets Big Smile

Emulateur arcade en Silverlight
http://www.innoveware.com/
Un émulateur arcade développé par Julien Frelat.

Emulateur PC en Silverlight
http://www.innoveware.com/
Un émulateur PC/Ms-Dos développé par Julien Frelat.

Démo en Silverlight 3 : Affichage d'un cube 3D en HD

Pour voir d'un peu plus près ce que Silverlight 3 a dans le ventre, j'ai fait une petite démo assez sympathique.

Cube 3D HD en Silverlight 3
Cliquez ici pour voir la démo.

Cette démo met en scène les fonctionnalités suivantes :

- Affichage d'une vidéo HD en 852x480 au format H.264 et AAC. La source utilisée est carrément un trailer disponible dans la gallerie HD de QuickTime 7 (.mov) qui est lu en streaming directement. Si si, c'est possible !

- Utilisation de l'accélération matérielle et du nouveau compteur de FPS pour voir les performances obtenues.

- Utilisation des effets de perspective 3D de Silverlight. C'est pas encore de la vraie 3D mais on s'en rapproche. Ici j'ai simulé l'affichage d'un cube (enfin un cube 16:9ème).

Voici un extrait du code source XAML :

<Canvas x:Name="Face1" Width="0" Height="0" Background="Black">
   <MediaElement x:Name="Video" Width="0" Height="0" Source="http://images.apple.com/movies/us/hd_gallery/gl1800/480p/batman_begins_m480p.mov" />
   <Canvas.Projection>
      <PlaneProjection RotationY="0" />
   </Canvas.Projection>
</Canvas>
<Canvas x:Name="Face2" Width="0" Height="0" Background="Black">
   <Image x:Name="Silverlight" Width="0" Height="0" Source="http://www.innoveware.com/ql3/silverlight.png" />
   <Canvas.Projection>
      <PlaneProjection RotationY="0" />
   </Canvas.Projection>
</Canvas>

J'utilise ici deux objets Canvas sur fond noir, l'un avec la vidéo H.264, l'autre avec une image de logo Silverlight. Il s'agit des deux faces principales du cube. Deux faces suffisent pour afficher le cube, inutile de s'encombrer de quatre faces (d'autant plus que la vidéo consomme beaucoup de ressources).

Voici maintenant un extrait du code source C# :

// Simulate cube faces with only two faces.
double rangle1 = (angle < 90 || angle > 270) ? 90 + angle : 270 + angle;
double rangle2 = (angle < 180) ? 360 + angle : 180 + angle;

// Compute first face position.
double x = distance * Math.Cos(DegreeToRadian(rangle1));
double z = distance * Math.Sin(DegreeToRadian(rangle1));

// Project face.
Face1.Projection.SetValue(PlaneProjection.RotationYProperty, rangle1 - 90);
Face1.Projection.SetValue(PlaneProjection.GlobalOffsetXProperty, x);
Face1.Projection.SetValue(PlaneProjection.GlobalOffsetZProperty, z - distance);

// Compute second face position.
x = distance * Math.Cos(DegreeToRadian(rangle2));
z = distance * Math.Sin(DegreeToRadian(rangle2));

// Project face.
Face2.Projection.SetValue(PlaneProjection.RotationYProperty, rangle2 - 90);
Face2.Projection.SetValue(PlaneProjection.GlobalOffsetXProperty, x);
Face2.Projection.SetValue(PlaneProjection.GlobalOffsetZProperty, z - distance);

Il s'agit de la boucle d'affichage principale. On va incrémenter l'angle de rotation Y du cube d'un degré en un degré. On détermine tout d'abord quelles sont les angles de projection des faces à afficher en alternance (je rappelle qu'on n'utilise que deux faces et non quatre).

Ensuite on utilise les propriétés GlobalOffsetX et GlobalOffsetZ de la projection perspective de Silverlight 3. C'est ce qui va nous permettre de déplacer le cube autour de son centre. Par défaut les objets 3D en Silverlight tournent sur eux-mêmes.

Cela correspond en fait à faire tourner nos coordonnées X et Z sur un cercle de rayon égal à la moitié de notre image. On positionne aussi le centre du cube sur l'axe des Z en arrière avec ce même rayon.

N.B: Pour l'instant, je n'ai pas réussi à faire mieux qu'un cube qui tourne sur un seul axe comme ici. Une double rotation impliquerait ici des calculs encore plus complexes. Mon prochain défi : une boule en 3D :)

Vidéo de ma session sur Quake en Silverlight aux TechDays 2009

Les vidéos des sessions des Microsoft TechDays 2009 étant maintenant disponibles, je vous invite à consulter la vidéo de ma session.

Il s'agit d'un petit making-of (rapide car le précédent speaker a un peu débordé) de mon projet Quakelight. Vous y trouverez des explications sur les techniques d'affichage bitmap et de gestion sonore que j'ai utilisés ainsi que deux démos.

Bien entendu, ça ne rend pas aussi bien que sur grand écran et une vidéo enregistré à 15 fps ne rend pas justice à Quake.

Astuce : placez vous à la moitié de la vidéo pour avoir le début de ma session. La première moitié c'est la session du speaker précédent Simon Ferquel...

http://www.microsoft.com/france/vision/mstechdays09/Webcast.aspx?eID=7e02b33e-702a-4a85-94af-dfbb178b2a28

Démo live de Quake en Silverlight 3

Si vous voulez tester les performances de Silverlight 3, j'ai mis à disposition sur mon site InnoveWare Solutions une démo de Quake en Silverlight. Elle utilise la version beta de Silverlight 3 donc elle est réservée uniquement aux développeurs. En effet, Microsoft a bien précisé qu'il n'y avait pas de license "Go Live" pour cette version Beta.

Le rendu est assuré par la nouvelle API WriteableBitmap (voir mon billet précédent pour les performances). La démo utilise également l'accélération matérielle et le nouveau framerate counter de Silverlight 3 :

<param name="EnableGPUAcceleration" value="true" />
<param name="EnableFrameRateCounter" value="true" />

Pour tester c'est ici.

Performances d'affichage de Silverlight 3

Comme vous le savez certainement, Silverlight 3 Beta est désormais téléchargeable en version de développement après l'annonce faîte au Salon MIX 09.

J'en ai profité pour faire quelques tests des nouvelles API et plus particulièrement celles concernant l'affichage bitmap et l'accélération matérielle (GPU).

Pour mon jeu de test, j'ai pris la version actuelle de mon Quake en Silverlight et une résolution basse (320x240).

Voici le résultat obtenu sans modification de code ni accélération GPU :

J'ai ensuite testé en activant l'accélération matérielle. Très simplement, il suffit d'ajouter le paramètre suivant à la page HTML.

<param name="EnableGPUAcceleration" value="true" />

Comme vous pouvez le constater, les performances sont nettement meilleures (25%) !

Concernant l'affichage bitmap de Quakelight, j'utilise actuellement une technique de PNG Wrapper (j'y reviendrai dans un prochain article). C'est à dire que je pré-encode chaque image dans un format PNG optimisé. Silverlight se charge ensuite de décoder cette image PNG pour l'afficher à l'écran. L'encodage est lui-même très performant si on le compare à d'autres techniques existantes (PNG Encoder), mais on stagne à la vitesse de Silverlight pour décoder le PNG...

Silverlight 3 propose une nouvelle API WriteableBitmap qui permet d'écrire des pixels dans une image directement. Autrement dit, ni encodage ni décodage PNG ne sont nécessaires. On devrait donc en théorie obtenir un gain de performance énorme. Allez zou on teste !

// On crée notre surface 320x240 au format Blue Green Red.
WriteableBitmap surface = new WriteableBitmap(320, 240, PixelFormats.Bgr32);

// On écrit un pixel dans le buffer (attention à l'ordre pour les couleurs)
surface.Lock();
surface[surface.PixelWidth * 160 + 120] = (RED << 16) | (GREEN << 8) | BLUE;
surface.Invalidate();
surface.Unlock();

Bon là j'avoue que j'y perds un peu mon latin. Les performances obtenues sont à vrai dire les mêmes qu'avec mon encodage/décodage en PNG d'origine. L'API est donc intéressante au niveau code mais ne permet pas de générer des images plus vite que ce que permet déjà Silverlight 2. Malheureusement, pas de Quake à 200 FPS ici...

En conclusion, je pense qu'il serait intéressant que je rende mes propres API compatibles avec Silverlight 3. Cela permettrait à tout le monde de les utiliser dans Silverlight 2 mais aussi d'offrir d'autres fonctionnalités au WriteableBitmap de Silverlight 3 (l'encodage PNG permet de supporter d'autres formats de pixel et donc par exemple des effets de palette).

Ouverture de mon blog Silverlight Underground :)

Bonjour à tous,

Bienvenue sur mon blog "Silverlight Underground" !

Je suis chef de projet et fondateur d'InnoveWare Solutions qui est à la fois une vitrine et un laboratoire personnel de mes différents projets. Je développe avec Microsoft Silverlight, une techno qui me tient beaucoup à coeur, depuis l'engouement de mon dernier projet en date Quakelight. Il s'agit ni plus ni moins que d'un portage du célèbre jeu Quake en C# et Silverlight !

Ce blog est consacré au développement en Silverlight, plus particulièrement tout ce qui concerne les jeux vidéos en ligne, l'émulation, le retrogaming... Vous y retrouverez des articles, du code et des astuces sur Microsoft Silverlight.

A très bientôt,

Julien Frelat

InnoveWare Solutions - Project Manager / CEO



Les 10 derniers blogs postés

- L’application des MiniDrones Parrot est aussi disponible pour Windows 8.1 par Blog de Jérémy Jeanson le 10-28-2014, 15:01

- L’application des MiniDrones Parrot est enfin disponible pour Windows Phone par Blog de Jérémy Jeanson le 10-27-2014, 09:49

- Mise à jour Samsung 840 EVO sur core server par Blog de Jérémy Jeanson le 10-27-2014, 05:59

- MVP Award 2014 ;) par Blog de Jérémy Jeanson le 10-27-2014, 05:42

- « Naviguer vers le haut » dans une librairie SharePoint par Blog de Jérémy Jeanson le 10-07-2014, 13:21

- PowerShell: Comment mixer NAGIOS et PowerShell pour le monitoring applicatif par Blog Technique de Romelard Fabrice le 10-07-2014, 11:43

- ReBUILD 2014 : les présentations par Le blog de Patrick [MVP Office 365] le 10-06-2014, 09:15

- II6 Management Compatibility présente dans Windows Server Technical Preview avec IIS8 par Blog de Jérémy Jeanson le 10-05-2014, 17:37

- Soft Restart sur Windows Server Technical Preview par Blog de Jérémy Jeanson le 10-03-2014, 19:43

- Non, le certificat public du CA n’est pas un certificat client !!! par Blog de Jérémy Jeanson le 10-03-2014, 00:08