I. Optimisation du vertex shader▲
Précédemment, j'ai publié un simple code d'exemple pour calculer la position et la normale dans le vertex shader en OpenGL (GLSL) et Direct3D (HLSL). Le but était de montrer le fonctionnement des différentes matrices : projection, view (vue) et model (modèle) pour déterminer la variable gl_Position et notamment l'ordre des multiplications des matrices avec OpenGL et Direct3D. Voici le vertex shader GLSL (nommé VS_A) :
uniform
mat4
M; // Matrice du modèle
uniform
mat4
V; // Matrice de vue
uniform
mat4
P; // Matrice de projection
void
main
(
)
{
vec4
v =
gl_Vertex;
mat4
MV =
V *
M;
mat4
MVP =
P *
MV;
vec4
v1 =
MVP *
v;
gl_Position
=
v1;
}
Cette méthode construit la matrice de transformation finale (MVP ou ModelViewProjection) et la multiplie avec la position du vertex dans l'espace local de l'objet (gl_Vertex). Cette méthode fonctionne mais génère un grand nombre d'instructions GPU.
J'ai démarré l'outil GPU Shader Analyzer et compilé le vertex shader précédent :
Une fois compilé, le vertex shader VS_A génère 49 instructions UAL(1).
Daniel Rakos m'a montré une solution plus rapide :
uniform
mat4
M; // Matrice du modèle
uniform
mat4
V; // Matrice de vue
uniform
mat4
P; // Matrice de projection
void
main
(
)
{
vec4
v =
gl_Vertex;
vec4
v1 =
M *
v;
vec4
v2 =
V *
v1;
vec4
v3 =
P *
v2;
gl_Position
=
v3;
}
Une fois compilé dans GPU ShaderAnalyzer, VS_B génère 13 instructions UAL.
J'ai rapidement effectué un test dans GeeXLab avec un objet (sphère) contenant deux millions de faces et un million de vertex :
- VS_A : 425 FPS ;
- VS_B : 425 FPS.
J'ai aussi effectué un test avec une simple démonstration OpenGL (une simple application de test win32) avec de l'instanciation géométrique (10 000 instances, 900 vertex par instance), sans trouver de différence entre les deux vertex shaders. Je m'attendais à une petite différence. Une explication peut être due à la simplicité de la scène. Je suis cependant sûr que nous allons voir une différence dans des shaders ou des scènes 3D plus complexes, car il doit y avoir un gain de performances entre 49 et 13 instructions ALU…
I-A. Mise à jour du 31 octobre 2011▲
Je pense avoir trouvé une réponse possible : le surcoût provoqué par l'appel du shader et/ou de l'accès aux vertex. En effet, si la charge de travail réelle du vertex shader est trop petite, la majorité du temps est perdue dans d'autres tâches telles que la récupération des vertex ou l'appel au shader ou, plus généralement, toute tâche venant avant ou après l'exécution du vertex shader. En conséquence, une différence d'une trentaine d'instructions ALU n'est pas visible.
Pour valider cette idée, j'ai augmenté le travail du vertex shader et comparé les deux vertex shaders suivants avec un objet constitué d'un million de vertex :
uniform
mat4
M; // Matrice du modèle
uniform
mat4
V; // Matrice de vue
uniform
mat4
P; // Matrice de projection
void
main
(
)
{
vec4
v =
gl_Vertex;
vec4
pos =
vec4
(
0
.0
);
for
(
int
i=
0
; i<
100
; i++
)
{
mat4
MV =
V *
M;
mat4
MVP =
P *
MV;
vec4
v1 =
MVP *
v;
pos +=
v1;
}
gl_Position
=
pos /
100
.0
;
}
et
uniform
mat4
M; // Matrice du modèle
uniform
mat4
V; // Matrice de vue
uniform
mat4
P; // Matrice de projection
void
main
(
)
{
vec4
v =
gl_Vertex;
vec4
pos =
vec4
(
0
.0
);
for
(
int
i=
0
; i<
100
; i++
)
{
vec4
v1 =
M *
v;
vec4
v2 =
V *
v1;
vec4
v3 =
P *
v2;
pos +=
v3;
}
gl_Position
=
pos /
100
.0
;
}
Avec le vertex shader A, la démonstration tourne à 19 FPS. Avec le vertex shader B, la démonstration tourne à 64 FPS.
Ouf, trouvé ! L'intuition était bonne : il y a une importante différence de vitesse entre ces deux vertex shaders.
II. Remerciements▲
Cet article est une traduction autorisée de l'article paru sur Geeks3D.com.
Je tiens à remercier Winjerome et dourouc05 pour leur relecture lors de la traduction de cet article, ainsi que ClaudeLELOUP et _Max_ pour leur relecture orthographique.