Étude sur les performances d'affichage de la SDL 1.2

Détermination de la meilleure méthode pour afficher des sprites avec la SDL 1.2

Alexandre Laurent

Souvent, la SDL est décriée. Elle est lente, principalement, car le rendu est effectué en n'utilisant que le CPU et absolument pas le GPU. Cet article tente donc de faire le point sur les performances de la bibliothèque.

Commentez

- Titre : Étude sur les performances d'affichage de la SDL 1.2

- Auteur : Alexandre Laurent

- Parution : 19 juin 2013

- Mise à jour : 19 juin 2013

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 LittleWhite. 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. Conditions de test

- II-A. Machine

- II-B. Logiciels

- III. Tests

- III-A. Test 1 - Événements

- III-A-1. Code

- III-A-2. Résultats

- III-A-3. Réflexion

- III-B. Test 2 - Échange des tampons

- III-B-1. Code

- III-B-2. Résultats

- III-B-3. Réflexion

- III-C. Test 3 - Nettoyage de l'écran

- III-C-1. Code

- III-C-2. Résultats

- III-C-3. Réflexion

- III-D. Test 4 - Nettoyage coloré de l'écran

- III-D-1. Code

- III-D-2. Résultats

- III-D-3. Réflexion

- III-E. Test 5 - Rectangles

- III-E-1. Code

- III-E-2. Résultats

- III-E-3. Réflexion

- III-F. Test 6 - Sprite BMP

- III-F-1. Code

- III-F-2. Résultats

- III-F-3. Réflexion

- III-G. Test 7 - Sprite BMP transparent

- III-G-1. Code

- III-G-2. Résultats

- III-G-3. Réflexion

- III-H. Test 8 - Conversion de sprite

- III-H-1. Code

- III-H-2. Résultats

- III-H-3. Réflexion

- III-I. Test 9 - Conversion de sprites transparents

- III-I-1. Code

- III-I-2. Résultats

- III-I-3. Réflexion

- III-J. Test 10 - PNG transparent

- III-J-1. Code

- III-J-2. Résultats

- III-J-3. Réflexion

- III-K. Test 11 - PNG transparent optimisé

- III-K-1. Code

- III-K-2. Résultats

- III-K-3. Réflexion

- III-L. Test 12 - JPG

- III-L-1. Code

- III-L-2. Résultats

- III-L-3. Réflexion

- III-M. Test 13 - JPG optimisé

- III-M-1. Code

- III-M-2. Résultats

- III-M-3. Réflexion

- IV. Optimisations

- IV-A. Test 2 optimisé

- IV-A-1. Code

- IV-A-2. Résultats

- IV-A-3. Impact

- IV-A-4. Réflexion

- IV-B. Test 6 optimisé

- IV-B-1. Code

- IV-B-2. Résultats

- IV-B-3. Impact

- IV-C. Test 8 optimisé

- IV-C-1. Code

- IV-C-2. Résultats

- IV-C-3. Impact

- V. Récapitulatif

- VI. Ce qu'il faut en retenir

- VII. Conclusion

- VIII. Que faut-il donc faire ?

- IX. Remerciements

I - Introduction

La bibliothèque Simple DirectMedia Layer est une bibliothèque incontournable pour les développeurs souhaitant démarrer dans la programmation d'un jeu vidéo. C'est une bibliothèque offrant tous les outils pour réaliser rapidement une application dynamique 2D et cela pour un grand nombre de plateformes (Windows, Mac OS, Linux, GP2X, DreamCast, Amiga…). Elle est simple à utiliser et est accessible à travers de nombreux de langages (C, C++, C#, Python…).

Toutefois, la version 1.2 vieillit et la version 2.0 vient d'arriver. Toutefois, revenons un peu sur l'ancienne version de la bibliothèque. Le défaut le plus cité est sa lenteur et l'impossibilité de gérer de grandes résolutions. En effet, la version 1.2 n'utilise pas le GPU, contrairement à la SFML, ou à Allegro 5. Mais cela n'est plus le cas grâce à la version 2.0 de la SDL.

Ayant moi-même utilisé la SDL pour de nombreux projets, je souhaitais revenir sur les performances de celle-ci, qui ne me semblaient pas si catastrophiques que cela. Voici donc, un article proposant une série de tests pour mesurer les performances de la bibliothèque et apprendre à mieux l'utiliser.

II - Conditions de test

II-A - Machine

Les tests sont effectués sur un PC portable équipé d'un processeur Intel i7 Q720, d'une NVIDIA GeForce GTS 360M et de huit gigaoctets de mémoire vive.

II-B - Logiciels

La machine fonctionne sous GNU/Linux, avec un noyau Linux 3.5.0. Les tests ont été compilés avec la version 1.2.15 de la SDL, la version 1.2.12 de SDL_image à l'aide de GCC 4.6.4 (avec les paramétrages d'optimisation -O3).

Les tests sont exécutés les un après les autres à l'aide d'un script python pour les résolutions suivantes :

- 640x480 ;

- 800x600 ;

- 1024x768 ;

- 1366x768 ;

- 1900x1200.

III - Tests

Tous les tests sont effectués en suivant le même algorithme :

Initialisation de la SDL (et de SDL_image si besoin)
Création de la fenêtre (et chargement du sprite si besoin)
tant que le test n'est pas fini
récupération des événements
dessin
copie du dessin à l'écran
si une seconde s'est écoulée depuis la dernière mise à jour
sauvegarde des compteurs d'images et de sprites
réinitialisation des compteurs
fin
fermeture de la SDL
sauvegarde des résultats dans un fichier

Chaque test ne diffère que très peu. En effet, seuls la méthode de chargement du sprite (qui n'est pas prise en compte par le test) et les éléments affichés changent.

Les tests sont exécutés pendant 50 secondes. À chaque seconde, le nombre d'images affichées en une seconde et le nombre de sprites (pour les tests se basant sur les sprites) sont sauvegardés dans un tableau (afin de ne par perdre de temps avec les entrées/sorties d'un fichier). Lors de la sauvegarde, une moyenne des résultats récupérés dans le test est effectuée afin d'avoir un résultat facilement exploitable.

Ci-dessous, un explicatif détaillé de chaque test.

III-A - Test 1 - Événements

Ce test ne fait pratiquement rien. Il exécute la boucle principale à vide, sans même alterner les tampons d'affichage. Finalement, ce test n'exécute que le traitement des événements de la SDL.

III-A-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
// On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

// Compteur de FPS
nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
nbFrames = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

III-A-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

1 344 840

1 334 179

1 310 083

1 336 968

1 324 169

III-A-3 - Réflexion

Il n'y a rien à conclure de ce test. Aucun affichage n'est effectué et ce test ne mesure que le temps pris par la SDL pour traiter les événements, ce qui est fait très rapidement vu qu'aucun événement ne se produit.

III-B - Test 2 - Échange des tampons

Ce test rajoute l'échange des tampons d'affichage au premier test.

III-B-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
//On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

SDL_Flip(pWindowSurface); // Nouvelle ligne

nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
nbFrames = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

III-B-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

1136

812

543

431

219

III-B-3 - Réflexion

Il est remarquable que les performances pour un programme ne faisant qu'un échange des tampons d'affichage soient autant impactées. Sachant que la SDL travaille uniquement du côté CPU, cette perte de temps est due au transfert des tampons du CPU vers l'écran (GPU).

III-C - Test 3 - Nettoyage de l'écran

Ce test ajoute le nettoyage de l'écran. Généralement, une boucle de rendu commence par le nettoyage de l'écran.

III-C-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);
Uint32 color = SDL_MapRGB(pWindowSurface->format,100,32,32);

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
//On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

SDL_FillRect(pWindowSurface,NULL, color); // Nouvelle ligne
SDL_Flip(pWindowSurface);

nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
nbFrames = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

III-C-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

770

552

351

271

128

III-C-3 - Réflexion

Le nettoyage de la surface de l'écran est lui aussi couteux, pénalisant une nouvelle fois, l'application.

III-D - Test 4 - Nettoyage coloré de l'écran

Afin de mieux connaître l'impact du nettoyage de l'écran et de chercher à savoir si des optimisations sont effectuées, ce quatrième test change de couleur de fond pour chaque image.

III-D-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);
// Détermination d'une couleur aléatoire
Uint32 color = SDL_MapRGB(pWindowSurface->format,rand()%255,rand()%255,rand()%255);

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
//On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

SDL_FillRect(pWindowSurface,NULL, color);
SDL_Flip(pWindowSurface);

nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
nbFrames = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

III-D-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

768

531

350

270

128

III-D-3 - Réflexion

Les performances mesurées entre le test 3 et le test 4 sont proches, indiquant qu'il n'y a pas d'optimisation suivant la couleur de nettoyage de l'écran (nous pouvions imaginer des optimisations pour le cas du noir).

III-E - Test 5 - Rectangles

Ce cinquième test se rapproche de ce que l'on trouve dans les jeux vidéo, mais à la place d'avoir des sprites, seuls des carrés colorés sont dessinés. La dimension des blocs est de 20x20 pixels.

III-E-1 - Code

Voici le code d'affichage des rectangles :

SDL_FillRect(pWindowSurface,NULL, color);

nbSprites=0;
for ( y = 0 ; y < HEIGHT ; y+=20 )
{
for ( x = 0 ; x < WIDTH ; x+=20 )
{
SDL_Rect spriteDest = { x,y,20,20 };
SDL_FillRect(pWindowSurface,&spriteDest,color2);
nbSprites++;
}
}
SDL_Flip(pWindowSurface);

III-E-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

519

371

225

188

83

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-E-3 - Réflexion

La SDL commence à montrer ses faiblesses avec la résolution 1900x1200. Alors que notre application est très simple et ne fait que dessiner des sprites uniformes, l'application est descendue en dessous de 100 FPS.

Dans un tel cas, afin de s'assurer de pouvoir afficher une application fluide avec une résolution 1900x1200, il sera conseillé de paralléliser l'application afin d'avoir un unique thread pour gérer l'affichage.

III-F - Test 6 - Sprite BMP

Enfin, nous affichons des sprites. Le sprite est chargé à partir d'un fichier BMP où chaque couleur est constituée de 24 bits, 8 pour chaque canal. Tout le sprite est affiché (49x98 pixels) mais seuls 20x20 seront visibles à chaque fois, car le reste est recouvert par les sprites suivants.

Pour les tests, il n'est pas très important de voir le sprite en entier sur l'image finale. De plus, cela permet de stresser la machine plus qu'il ne le faut, en affichant plus de sprites qu'il ne serait possible d'aligner sans superposer l'image d'origine.

III-F-1 - Code

Fonction de chargement du sprite :

SDL_Surface* loadBMPSprite(const char* spritePath)
{
SDL_Surface* pSurface = SDL_LoadBMP(spritePath);
if ( pSurface == NULL )
{
fprintf(stderr,"Fail to load sprite '%s'\n",spritePath);
}

return pSurface;
}

L'affichage du sprite ne change pas du test précédent.

III-F-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

22

13

7

5

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-F-3 - Réflexion

Pour toutes les résolutions testées, notre application n'est pas fluide. Toutefois, l'application peut être améliorée.

III-G - Test 7 - Sprite BMP transparent

Comme le sprite BMP n'embarque pas de composante transparente, la couleur rose (RGB : 255, 0, 255) utilisée pour le fond, peut être indiquée à la SDL afin qu'elle ne soit finalement pas utilisée comme telle, mais comme une absence de couleur (donc vous ne verrez pas du rose à l'écran, mais la couleur en dessous de votre sprite pour tous les pixels roses).

III-G-1 - Code

SDL_Surface* loadBMPSpriteColorKey(const char* spritePath)
{
SDL_Color transparencyColour = {255, 0, 255, 255};

SDL_Surface* pSurface = SDL_LoadBMP(spritePath);
if ( pSurface != NULL )
{
// Nous définissons une couleur de transparence
Uint32 colorkey = SDL_MapRGB(pSurface->format, transparencyColour.r, transparencyColour.g, transparencyColour.b);
if ( SDL_SetColorKey(pSurface, SDL_SRCCOLORKEY, colorkey ) == -1 )
{
fprintf(stderr,"Fail to set transparency in '%s'\n",spritePath);
}
else
{
fprintf(stderr,"Fail to optimise sprite '%s'\n",spritePath);
}
}

return pSurface;
}

III-G-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

41

25

14

11

5

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-G-3 - Réflexion

Indirectement, les performances sont meilleures. Il est probable qu'en rajoutant la transparence et sa gestion, le format des couleurs du sprite se rapproche de la surface de l'écran. Ce n'est toutefois pas suffisant pour une application fluide.

III-H - Test 8 - Conversion de sprite

La SDL propose de convertir et d'optimiser les sprites avec la fonction SDL_DisplayFormat(). Le sprite est modifié afin d'être au même format que la surface destination (l'écran), évitant ainsi les conversions de format de couleur pendant le blit.

III-H-1 - Code

SDL_Surface* loadBMPSpriteOptimised(const char* spritePath)
{
SDL_Color transparencyColour = {0, 0, 0, 0};

SDL_Surface* pSurface = SDL_LoadBMP(spritePath);
if ( pSurface != NULL )
{
// Nous optimisons la texture pour correspondre avec le format de l'écran
SDL_Surface* pOptimisedSurface = SDL_DisplayFormat(pSurface);
if ( pOptimisedSurface != NULL )
{
// Nous libérons la mémoire associée à l'ancienne surface
SDL_FreeSurface(pSurface);

// Nous définissons une couleur de transparence
Uint32 colorkey = SDL_MapRGB(pOptimisedSurface->format, transparencyColour.r, transparencyColour.g, transparencyColour.b);
if ( SDL_SetColorKey(pOptimisedSurface, SDL_RLEACCEL, colorkey ) == -1 )
{
fprintf(stderr,"Fail to set transparency in '%s'\n",spritePath);
}

// Nous remplaçons notre pointeur final par le pointeur sur la surface optimisée
pSurface = pOptimisedSurface;
}
else
{
fprintf(stderr,"Fail to optimise sprite '%s'\n",spritePath);
}
}
else
{
fprintf(stderr,"Fail to load sprite '%s'\n",spritePath);
}

return pSurface;
}

III-H-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

98

61

39

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-H-3 - Réflexion

Le gain de performance n'est pas négligeable et cette amélioration permet d'avoir une application fluide même lors de l'affichage d'un millier de sprites.

III-I - Test 9 - Conversion de sprites transparents

Les deux méthodes utilisées dans le test 7 et le test 8 sont maintenant utilisées conjointement afin d'obtenir un sprite donnant un résultat correct (grâce à la couleur de transparence) tout en étant optimisé avec la fonction SDL_DisplayFormat.

III-I-1 - Code

SDL_Surface* loadBMPSpriteColorKeyOptimised(const char* spritePath)
{
SDL_Color transparencyColour = {255, 0, 255, 255};

SDL_Surface* pSurface = SDL_LoadBMP(spritePath);
if ( pSurface != NULL )
{
// Nous optimisons la texture pour correspondre avec le format de l'écran
SDL_Surface* pOptimisedSurface = SDL_DisplayFormat(pSurface);
if ( pOptimisedSurface != NULL )
{
// Nous libérons la mémoire associée à l'ancienne surface
SDL_FreeSurface(pSurface);

// Nous définissons une couleur de transparence
Uint32 colorkey = SDL_MapRGB(pOptimisedSurface->format, transparencyColour.r, transparencyColour.g, transparencyColour.b);
if ( SDL_SetColorKey(pOptimisedSurface, SDL_RLEACCEL | SDL_SRCCOLORKEY, colorkey ) == -1 )
{
fprintf(stderr,"Fail to set transparency in '%s'\n",spritePath);
}

// Nous remplaçons notre pointeur final par le pointeur sur la surface optimisée
pSurface = pOptimisedSurface;
}
else
{
fprintf(stderr,"Fail to optimise sprite '%s'\n",spritePath);
}
}
else
{
fprintf(stderr,"Fail to load sprite '%s'\n",spritePath);
}

return pSurface;
}

III-I-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

116

93

50

41

22

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-I-3 - Réflexion

Finalement, pour la plupart des applications, nous retrouvons des performances raisonnables. Seule la résolution 1900x1200 résulte en une application peu fluide.

III-J - Test 10 - PNG transparent

La bibliothèque SDL_image est très pratique pour charger des fichiers images autres que des BMP. Dans ce test, nous chargeons un PNG possédant un canal transparent.

III-J-1 - Code

Fonction de chargement basique d'un fichier PNG en utilisant SDL_image :

SDL_Surface* loadSDLImageSprite(const char* spritePath)
{
SDL_Surface* pSprite = IMG_Load(spritePath);
if ( !pSprite )
{
fprintf(stderr,"Fail to load sprite '%s' -> '%s'\n",spritePath,IMG_GetError());
}

return pSprite;
}

III-J-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

12

7

4

3

1

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-J-3 - Réflexion

L'utilisation d'un sprite PNG donne des résultats épouvantables. Même si la bibliothèque SDL_image est simple à utiliser et facilite le chargement de l'image, elle sera à éviter pour un jeu nécessitant de nombreux éléments actifs sur l'écran.

III-K - Test 11 - PNG transparent optimisé

Une question à poser et de savoir s'il est possible d'améliorer les performances de l'affichage d'une image chargée à partir d'un fichier PNG. Ce test essaie d'optimiser le sprite avec la fonction SDL_DisplayFormat().

III-K-1 - Code

Fonction de chargement d'un fichier PNG avec optimisation du sprite :

SDL_Surface* loadSDLImageSpriteOptimised(const char* spritePath)
{
SDL_Color transparencyColour = {0, 0, 0, 0};

SDL_Surface* pSurface = IMG_Load(spritePath);
if ( pSurface != NULL )
{
// Nous optimisons la texture pour correspondre avec le format de l'écran
SDL_Surface* pOptimisedSurface = SDL_DisplayFormat(pSurface);
if ( pOptimisedSurface != NULL )
{
// Nous libérons la mémoire associée à l'ancienne surface
SDL_FreeSurface(pSurface);

// Nous définissons une couleur de transparence
Uint32 colorkey = SDL_MapRGB(pOptimisedSurface->format, transparencyColour.r, transparencyColour.g, transparencyColour.b);
if ( SDL_SetColorKey(pOptimisedSurface, SDL_RLEACCEL, colorkey ) == -1 )
{
fprintf(stderr,"Fail to set transparency in '%s'\n",spritePath);
}

// Nous remplaçons notre pointeur final par le pointeur sur la surface optimisée
pSurface = pOptimisedSurface;
}
else
{
fprintf(stderr,"Fail to optimise sprite '%s'\n",spritePath);
}
}
else
{
fprintf(stderr,"Fail to load sprite '%s'\n",spritePath);
}

return pSurface;
}

III-K-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

99

58

38

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-K-3 - Réflexion

Même si l'utilisation du SDL_DisplayFormat() pour optimiser la surface apporte un plus, les performances restent basses.

III-L - Test 12 - JPG

Ce test est équivalent au dixième test, mais à la place de charger une image PNG, une image JPG est chargée.

III-L-1 - Code

Le code de chargement et d'affichage est identique au dixième test. Seul le fichier d'images source change.

III-L-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

21

13

7

6

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-L-3 - Réflexion

Tout comme pour le PNG, l'utilisation des JPG, sans optimisation est un gouffre de performances ne permettant pas de réaliser une application fluide.

III-M - Test 13 - JPG optimisé

Ce test est équivalent au onzième test, mais à la place de charger une image PNG, une image JPG est chargée.

III-M-1 - Code

Le code de chargement et d'affichage est identique au onzième test. Seul le fichier d'images source change.

III-M-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Nombre d'images par seconde

99

59

39

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

III-M-3 - Réflexion

On retrouve ici les mêmes performances qu'avec l'utilisation d'image PNG.

IV - Optimisations

Quelques optimisations sont possibles et il est donc nécessaire de voir leur impact sur les performances.

IV-A - Test 2 optimisé

La fonction SDL_SetVideoMode() possède un indicateur SDL_HWSURFACE permettant de créer les surfaces directement sur la mémoire vidéo.

IV-A-1 - Code

// Créer la fenêtre
pWindowSurface = SDL_SetVideoMode(WIDTH,HEIGHT,32,SDL_HWSURFACE | SDL_DOUBLEBUF);
if ( pWindowSurface == NULL )
{
fprintf(stderr,"Erreur lors de l'ouverture de la fenêtre\n");
SDL_Quit();
return -2;
}

IV-A-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Test 2

Nombre d'images par seconde

1136

812

543

431

219

Test 2 optimisé

1135

811

552

431

219

IV-A-3 - Impact

Sous Linux, le changement entre SDL_SWSURFACE et SDL_HWSURFACE est négligeable.

IV-A-4 - Réflexion

Malheureusement, la création de surface dans la mémoire vidéo est rarement implémentée (comme ici, sous Linux où cela ne l'est pas) et l'est uniquement pour quelques plateformes. La GP2X, par exemple, bénéficie d'une accélération.

IV-B - Test 6 optimisé

Sachant que le test recouvre toute la surface et que l'on ne voit donc pas le fond, il est possible d'enlever la ligne de nettoyage de l'écran.

IV-B-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);

unsigned int x = 0;
unsigned int y = 0;

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
//On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

nbSprites=0;
for ( y = 0 ; y < HEIGHT ; y+=20 )
{
for ( x = 0 ; x < WIDTH ; x+=20 )
{
SDL_Rect spriteDest = { x,y,pSprite->w,pSprite->h };
SDL_BlitSurface(pSprite,NULL,pWindowSurface,&spriteDest);
nbSprites++;
}
}
SDL_Flip(pWindowSurface);

nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
results[resultsCounter].nbSprites = nbSprites;
nbFrames = 0;
nbSprites = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

IV-B-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Test 6 optimisé

Nombre d'images par seconde

22

13

7

5

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 6 optimisé

Nombre d'images par seconde

21

13

7

5

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

IV-B-3 - Impact

La suppression du nettoyage de l'écran n'affecte en rien les performances.

IV-C - Test 8 optimisé

Ce test reprend l'optimisation du test 6 et ne nettoie pas l'écran du fond.

IV-C-1 - Code

// Boucle principale
while ( mustContinue && resultsCounter < NB_ITERATION )
{
SDL_Event event;
SDL_PollEvent(&event);

unsigned int x = 0;
unsigned int y = 0;

// Détecte si on clique sur la croix
if( event.type == SDL_QUIT )
{
//On quitte le programme
mustContinue = 0;
}
// Détecte si une touche est appuyée
else if( event.type == SDL_KEYDOWN )
{
if ( event.key.keysym.sym == SDLK_ESCAPE )
{
mustContinue = 0;
}
}

nbSprites=0;
for ( y = 0 ; y < HEIGHT ; y+=20 )
{
for ( x = 0 ; x < WIDTH ; x+=20 )
{
SDL_Rect spriteDest = { x,y,pSprite->w,pSprite->h };
SDL_BlitSurface(pSprite,NULL,pWindowSurface,&spriteDest);
nbSprites++;
}
}
SDL_Flip(pWindowSurface);

nbFrames++;
if ( SDL_GetTicks()-lastUpdate > 1000 )
{
results[resultsCounter].nbFrames = nbFrames;
results[resultsCounter].nbSprites = nbSprites;
nbFrames = 0;
nbSprites = 0;
lastUpdate+=1000;

resultsCounter++;
}
}

IV-C-2 - Résultats

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Test 8

Nombre d'images par seconde

98

61

39

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 8 optimisé

Nombre d'images par seconde

99

63

39

30

15

Nombre de sprites par seconde

768

1200

2028

2691

5700

IV-C-3 - Impact

La suppression du nettoyage de l'écran n'affecte en rien les performances.

V - Récapitulatif

Résolution

640x480

800x600

1024x768

1366x768

1900x1200

Test 1 - Événements

Nombre d'images par seconde

1 344 840

1 334 179

1 310 083

1 336 968

1 324 169

Test 2 - Échange des tampons

Nombre d'images par seconde

1136

812

543

431

219

Test 2 optimisé

Nombre d'images par seconde

1135

811

552

431

219

Test 3 - Nettoyage de l'écran

Nombre d'images par seconde

770

552

351

271

128

Test 4 - Nettoyage coloré de l'écran

Nombre d'images par seconde

768

531

350

270

128

Test 5 - Rectangles

Nombre d'images par seconde

519

371

225

188

83

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 6 - Sprite BMP

Nombre d'images par seconde

22

13

7

5

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 6 - optimisé

Nombre d'images par seconde

21

13

7

5

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 7 - Sprite BMP transparent

Nombre d'images par seconde

41

25

14

11

5

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 8 - Conversion de sprites

Nombre d'images par seconde

98

61

39

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 8 optimisé

Nombre d'images par seconde

99

63

39

30

15

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 9 - Conversion de sprites transparents

Nombre d'images par seconde

116

93

50

41

22

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 10 - PNG transparent

Nombre d'images par seconde

12

7

4

3

1

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 11 - PNG transparent optimisé

Nombre d'images par seconde

99

58

38

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 12 - JPG

Nombre d'images par seconde

21

13

7

6

2

Nombre de sprites par seconde

768

1200

2028

2691

5700

Test 13 - JPG optimisé

Nombre d'images par seconde

99

59

39

29

14

Nombre de sprites par seconde

768

1200

2028

2691

5700

VI - Ce qu'il faut en retenir

À l'aide de ce test, il a été possible de déterminer l'impact de l'affichage des sprites et la nécessité de les optimiser à l'aide de la fonction SDL_DisplayFormat(). De plus, l'utilisation de SDL_image, pour charger des fichiers images autres que des BMP réduit considérablement les performances de l'application. Lorsque l'on utilise la SDL, il est clairement avantageux de garder les ressources au format BMP.

Afin que le programme final ne prenne pas une place extraordinaire sur le disque, il peut être utile de compresser les BMP et de les décompresser au lancement du jeu.

VII - Conclusion

La SDL n'est pas une si mauvaise bibliothèque que ce que l'on dit. Il est possible de faire un jeu avec, même dans des résolutions comme 1024x768. Les tests ont été effectués de façon à afficher plus de sprites qu'il n'en faut pour remplir l'écran, ce qui n'arrive que très rarement dans les jeux (même les shoot em up ne verront pas 5000 sprites affichés à l'écran, comme cela a été fait pour la résolution 1900x1200).

VIII - Que faut-il donc faire ?

La bibliothèque pousse à réfléchir aux solutions pouvant être mises en place pour garder un jeu 2D fluide. Par exemple, lorsque cela est possible, il pourrait être intéressant d'éviter d'afficher un sprite déjà à l'écran. Une autre idée sera de paralléliser le jeu afin de garder un thread uniquement pour l'affichage du jeu et le reste pour la gestion des éléments de ce jeu.

IX - Remerciements

Je tiens à remercier Kannagi pour m'avoir soufflé l'envie de faire ces expérimentations. De plus, je remercie Winjerome pour ses retours et ClaudeLELOUP pour ses corrections orthographiques.