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 testTest 10, 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 testTest 10. 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 testTest 11, 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 testTest 11. 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énementsTest 1 | Nombre d'images par seconde | 1 344 840 | 1 334 179 | 1 310 083 | 1 336 968 | 1 324 169 |
Test 2 - Échange des tamponsTest 2 | Nombre d'images par seconde | 1136 | 812 | 543 | 431 | 219 |
Test 2 optimiséTest 2 optimisé | Nombre d'images par seconde | 1135 | 811 | 552 | 431 | 219 |
Test 3 - Nettoyage de l'écranTest 3 | Nombre d'images par seconde | 770 | 552 | 351 | 271 | 128 |
Test 4 - Nettoyage coloré de l'écranTest 4 | Nombre d'images par seconde | 768 | 531 | 350 | 270 | 128 |
Test 5 - RectanglesTest 5 | Nombre d'images par seconde | 519 | 371 | 225 | 188 | 83 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 6 - Sprite BMPTest 6 | Nombre d'images par seconde | 22 | 13 | 7 | 5 | 2 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 6 - optimisé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 transparentTest 7 | Nombre d'images par seconde | 41 | 25 | 14 | 11 | 5 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 8 - Conversion de spritesTest 8 | Nombre d'images par seconde | 98 | 61 | 39 | 29 | 14 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 8 optimisé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 transparentsTest 9 | Nombre d'images par seconde | 116 | 93 | 50 | 41 | 22 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 10 - PNG transparentTest 10 | 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éTest 11 | Nombre d'images par seconde | 99 | 58 | 38 | 29 | 14 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 12 - JPGTest 12 | Nombre d'images par seconde | 21 | 13 | 7 | 6 | 2 |
Nombre de sprites par seconde | 768 | 1200 | 2028 | 2691 | 5700 | |
Test 13 - JPG optimiséTest 13 | 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.