2017-04-03 2 views
2

Je travaille sur une application Android intégrée dans Unity3D qui doit créer de nouvelles textures à l'exécution de temps en temps en fonction de différentes données de pixels d'images. Comme Unity pour Android utilise OpenGL ES et que mon application est une application graphique qui doit fonctionner à 60 images par seconde, j'ai créé un plugin C++ fonctionnant avec du code OpenGL au lieu d'utiliser la texture lente Texture2D d'Unity construction. Le plugin me permet de télécharger les données de pixels dans une nouvelle texture OpenGL, puis d'en informer Unity via la fonction CreateExternalTexture() de leur texture2D. Étant donné que la version d'OpenGL ES fonctionnant dans cette configuration est malheureusement monothread, pour faire fonctionner les choses dans le cadre, je fais un appel glTexImage2D() avec un TextureID déjà généré mais avec des données nulles dans la première image . Et puis appelez glTexSubImage2D() avec une section de mon tampon de données de pixel, sur plusieurs images suivantes pour remplir la texture entière, essentiellement en faisant la création de texture de manière synchrone mais en segmentant l'opération sur plusieurs images! Maintenant, le problème que j'ai est que chaque fois que je crée une nouvelle texture avec de grandes dimensions, ce premier appel glTexImage2D() mènera toujours à un cadre, même si je mets des données nulles dans il. Je suppose que la raison en est qu'il y a toujours une allocation de mémoire assez importante en arrière-plan avec ce premier appel glTexImage2D(), même si je ne remplis l'image que plus tard.Façon la plus efficace de créer de grandes textures à l'exécution dans OpenGL ES pour Android

Malheureusement, ces images pour lesquelles je crée des textures sont de tailles différentes que je ne connaissais pas auparavant et donc je ne peux pas créer juste un tas de textures à l'avant sur la charge, je dois spécifier une nouvelle largeur et hauteur avec chaque nouvelle texture à chaque fois. =

Y a-t-il un moyen d'éviter cette allocation de mémoire, d'allouer un énorme bloc de mémoire au début et de l'utiliser comme pool pour de nouvelles textures? je l'ai mal compris, mais il me semblait que vous avez encore besoin de faire un glTexImage2D() pour affecter la texture avant de le fixer à l'OIR?

tout et tous les conseils est la bienvenue, merci d'avance! =)

PS: Je ne viens pas d'un arrière-plan graphique, donc je ne suis pas au courant des meilleures pratiques avec OpenGL ou d'autres bibliothèques graphiques, j'essaie juste de créer de nouvelles textures à l'exécution sans cadrage!

Répondre

1

Je n'ai pas traité le problème spécifique que vous avez rencontré, mais j'ai trouvé que les pools de textures étaient extrêmement utiles dans OpenGL pour obtenir des résultats efficaces sans avoir à y penser beaucoup. Dans mon cas, le problème était que je ne pouvais pas utiliser la même texture pour une entrée dans un shader différé que la texture utilisée pour sortir les résultats. Pourtant, je voulais souvent faire exactement cela:

// Make the texture blurry. 
blur(texture); 

Pourtant, au lieu que j'avais pour créer 11 textures différentes avec différentes résolutions et d'avoir à échanger entre eux des entrées et des sorties pour les shaders de flou horizontal/vertical avec les organisations confessionnelles pour obtenir un flou à l'aspect décent. Je n'ai jamais vraiment aimé la programmation GPU car certains des cas de gestion d'état les plus complexes que j'ai rencontrés étaient souvent là. Il me semblait incroyablement mauvais que je devais aller à la planche à dessin juste pour comprendre comment minimiser le nombre de textures allouées en raison de cette exigence fondamentale que les entrées de texture pour les shaders ne puissent pas être utilisées comme sorties de texture.

J'ai donc créé un pool de texture et OMG, ça a tellement simplifié les choses!Il a fait en sorte que je puisse simplement créer des objets de texture temporaires à gauche et à droite et ne pas m'inquiéter car la destruction de l'objet de texture n'appelle pas réellement glDeleteTextures, il les renvoie simplement au pool. J'ai donc pu enfin faire:

blur(texture); 

... comme je le voulais depuis toujours. Et pour une raison amusante, quand j'ai commencé à utiliser la piscine de plus en plus, elle a accéléré les taux de trame. Je suppose que même avec toute la pensée que j'ai mise en minimisant le nombre de textures allouées, j'allais toujours allouer plus que nécessaire de manière à éliminer le pool (notez que l'exemple du monde réel fait bien plus que du flou, y compris DOF, bloom, hipass, lowpass, CMAA, etc, et le code GLSL est en fait généré à la volée sur la base d'un langage de programmation visuel que les utilisateurs peuvent utiliser pour créer de nouveaux shaders à la volée).

Donc, je recommande vraiment de commencer par explorer cette idée. Il semble que ce serait utile pour votre problème. Dans mon cas, je ceci:

struct GlTextureDesc 
{ 
    ... 
}; 

... et c'est une structure assez lourde étant donné le nombre de paramètres texture, nous pouvons spécifier (format de pixels, le nombre de composantes de couleur, niveau LOD, largeur, hauteur, etc., etc. .).

Pourtant, la structure est comparable et lavable et finit par être utilisée comme une clé dans une table de hachage (comme unordered_multimap) avec la poignée de texture réelle comme valeur associée.

Cela nous permet de faire alors ceci:

// Provides a pool of textures. This allows us to conveniently rapidly create, 
// and destroy texture objects without allocating and freeing an excessive number 
// of textures. 
class GlTexturePool 
{ 
public: 
    // Creates an empty pool. 
    GlTexturePool(); 

    // Cleans up any textures which haven't been accessed in a while. 
    void cleanup(); 

    // Allocates a texture with the specified properties, retrieving an existing 
    // one from the pool if available. The function returns a handle to the 
    // allocated texture. 
    GLuint allocate(const GlTextureDesc& desc); 

    // Returns the texture with the specified key and handle to the pool. 
    void free(const GlTextureDesc& desc, GLuint texture); 

private: 
    ... 
}; 

à quel point nous pouvons créer des objets temporaires de texture gauche et à droite sans se soucier des appels excessifs à glTexImage2D et glDeleteTextures .Je trouve cela extrêmement utile. Enfin, notons que cleanup fonctionne ci-dessus. Lorsque je stocke des textures dans la table de hachage, je leur mets un horodatage (en utilisant le système en temps réel). Périodiquement, j'appelle cette fonction de nettoyage qui analyse ensuite les textures dans la table de hachage et vérifie l'horodatage. Si une certaine période de temps s'est écoulée alors qu'ils sont juste assis au ralenti dans la piscine (disons 8 secondes), j'appelle glDeleteTextures et les retire de la piscine. J'utilise un thread séparé avec une variable de condition pour créer une liste de textures à supprimer la prochaine fois qu'un contexte valide est disponible en analysant périodiquement la table de hachage, mais si votre application est entièrement monothread, vous pouvez invoquer ce nettoyage Fonctionne toutes les quelques secondes dans votre boucle principale. Cela dit, je travaille en VFX qui n'a pas des exigences en temps réel aussi serrées que, disons, les jeux AAA. Il y a plus d'accent sur le rendu hors ligne dans mon domaine et je suis loin d'un assistant GPU. Il pourrait y avoir de meilleurs moyens de s'attaquer à ce problème. Cependant, j'ai trouvé très utile de commencer avec ce pool de texture et je pense qu'il pourrait être utile dans votre cas aussi. Et c'est assez trivial à mettre en œuvre (il m'a juste pris une demi-heure environ). Cela pourrait encore finir par allouer et supprimer des lots et beaucoup de textures si les tailles de texture et les formats et les paramètres que vous demandez d'allouer/free sont partout. Là, il pourrait aider à unifier les choses un peu, comme au moins en utilisant POT (puissance de deux) tailles et ainsi de suite et de décider sur un nombre minimum de formats de pixels à utiliser. Dans mon cas ce n'était pas vraiment un problème car je n'utilise qu'un seul format de pixel et la majorité des temporaires de texture que je voulais créer sont exactement la taille d'une fenêtre agrandie au plafond POT. Comme pour les FBO, je ne sais pas comment ils aident votre problème immédiat avec l'allocation/la libération excessive de texture non plus.Je les utilise principalement pour l'ombrage différé pour effectuer un post-traitement pour des effets tels que DOF après avoir rendu la géométrie en plusieurs passes d'une manière de type compositing appliquée aux textures 2D qui en résultent. J'utilise les FBO pour cela naturellement mais je ne peux pas penser à la façon dont les FBO réduisent immédiatement le nombre de textures que vous devez allouer/désallouer, sauf si vous pouvez simplement utiliser une grande texture avec un FBO et lui rendre plusieurs textures d'entrée vers une sortie hors écran texture. Dans ce cas, les FBO ne seraient pas directement en mesure de créer une texture énorme dont les sections peuvent être utilisées comme entrées/sorties au lieu de beaucoup plus petites.