J'ai une application iOS open source qui utilise des shaders OpenGL ES 2.0 personnalisés pour afficher des représentations 3D de structures moléculaires. Pour ce faire, elle utilise des imposteurs de sphères et de cylindres générés de manière procédurale et dessinés sur des rectangles, au lieu de ces mêmes formes construites à l'aide de nombreux sommets. L'inconvénient de cette approche est que les valeurs de profondeur pour chaque fragment de ces objets imposteurs doivent être calculées dans un fragment shader, pour être utilisées lorsque les objets se chevauchent.
Malheureusement, OpenGL ES 2.0 ne vous permet pas d'écrire dans gl_FragDepth J'ai donc eu besoin de transmettre ces valeurs à une texture de profondeur personnalisée. J'effectue un passage sur ma scène en utilisant un objet framebuffer (FBO), ne rendant qu'une couleur correspondant à une valeur de profondeur, les résultats étant stockés dans une texture. Cette texture est ensuite chargée dans la deuxième partie de mon processus de rendu, où l'image d'écran réelle est générée. Si un fragment à ce stade se trouve au niveau de profondeur stocké dans la texture de profondeur pour ce point de l'écran, il est affiché. Dans le cas contraire, il est jeté. Pour plus d'informations sur le processus, y compris des diagrammes, consultez mon article. aquí .
La génération de cette texture de profondeur est un goulot d'étranglement dans mon processus de rendu et je cherche un moyen de l'accélérer. Elle semble plus lente qu'elle ne devrait l'être, mais je n'arrive pas à comprendre pourquoi. Afin d'obtenir la génération correcte de cette texture de profondeur, GL_DEPTH_TEST
est désactivé, GL_BLEND
est activé avec glBlendFunc(GL_ONE, GL_ONE)
y glBlendEquation()
est réglé sur GL_MIN_EXT
. Je sais qu'une scène produite de cette manière n'est pas la plus rapide sur un système de rendu différé basé sur des tuiles comme la série PowerVR des appareils iOS, mais je ne vois pas de meilleur moyen de le faire.
Mon shader de fragment de profondeur pour les sphères (l'élément d'affichage le plus courant) semble être au cœur de ce goulot d'étranglement (l'utilisation du moteur de rendu dans Instruments est évaluée à 99 %, ce qui indique que je suis limité par le traitement des fragments). La situation actuelle est la suivante :
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
const vec3 stepValues = vec3(2.0, 1.0, 0.0);
const float scaleDownFactor = 1.0 / 255.0;
void main()
{
float distanceFromCenter = length(impostorSpaceCoordinate);
if (distanceFromCenter > 1.0)
{
gl_FragColor = vec4(1.0);
}
else
{
float calculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter);
mediump float currentDepthValue = normalizedDepth - adjustedSphereRadius * calculatedDepth;
// Inlined color encoding for the depth values
float ceiledValue = ceil(currentDepthValue * 765.0);
vec3 intDepthValue = (vec3(ceiledValue) * scaleDownFactor) - stepValues;
gl_FragColor = vec4(intDepthValue, 1.0);
}
}
Sur un iPad 1, il faut 35 à 68 ms pour rendre une image d'un modèle de remplissage de l'espace par l'ADN en utilisant un shader passthrough pour l'affichage (18 à 35 ms sur un iPhone 4). D'après le compilateur PowerVR PVRUniSCo (qui fait partie de l'application leur SDK ), ce shader utilise 11 cycles GPU au mieux, 16 cycles au pire. Je suis conscient que l'on vous conseille de ne pas utiliser de branchement dans un shader, mais dans ce cas, cela a conduit à de meilleures performances qu'autrement.
Quand je le simplifie en
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
void main()
{
gl_FragColor = vec4(adjustedSphereRadius * normalizedDepth * (impostorSpaceCoordinate + 1.0) / 2.0, normalizedDepth, 1.0);
}
il faut 18 à 35 ms sur l'iPad 1, mais seulement 1,7 à 2,4 ms sur l'iPhone 4. Le nombre de cycles GPU estimé pour ce shader est de 8 cycles. La modification du temps de rendu en fonction du nombre de cycles ne semble pas linéaire.
Enfin, si je sors juste une couleur constante :
precision mediump float;
void main()
{
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}
le temps de rendu tombe à 1,1 - 2,3 ms sur l'iPad 1 (1,3 ms sur l'iPhone 4).
L'échelle non linéaire du temps de rendu et le changement soudain entre l'iPad et l'iPhone 4 pour le deuxième shader me font penser qu'il y a quelque chose qui m'échappe ici. Un projet source complet contenant ces trois variantes de shaders (regardez dans le fichier SphereDepth.fsh et commentez les sections appropriées) et un modèle de test peut être téléchargé à l'adresse suivante aquí si vous souhaitez essayer vous-même.
Si vous avez lu jusqu'ici, ma question est la suivante : sur la base de ces informations de profilage, comment puis-je améliorer les performances de rendu de mon shader de profondeur personnalisé sur les appareils iOS ?