Aller au contenu

[Résolu] [OpenGL] Limites mémoire.


The_Moye

Recommended Posts

Yop yop!

Voila, il y a quelques jours j'ai commencé de créer une petite application OpenGL qui pour le moment me permet d'afficher mes .obj et du texte (je suis en train de bosser sur l'UI).

Je n'ai aucun soucis à charger un objet texturé de l'ordre de 200 faces (triangles), l'appli reste fluide.

Histoire de stresser un peu plus mon Hero, j'ai tenté de charger un objet de 800 faces.

Voici les messages que j'obtiens lors du chargement (lecture du fichier .obj et remplissage des VBO OpenGL) :

09-21 18:38:32.458: INFO/System.out(731): On va creer le vaisseau

09-21 18:38:32.878: DEBUG/dalvikvm(731): GC freed 1033 objects / 135616 bytes in 338ms

09-21 18:38:33.539: DEBUG/dalvikvm(731): GC freed 7694 objects / 425232 bytes in 103ms

09-21 18:38:34.438: DEBUG/dalvikvm(731): GC freed 14475 objects / 499152 bytes in 133ms

09-21 18:38:34.648: DEBUG/dalvikvm(571): GC freed 4875 objects / 213864 bytes in 225ms

09-21 18:38:35.568: DEBUG/dalvikvm(731): GC freed 15193 objects / 533264 bytes in 106ms

09-21 18:38:36.788: DEBUG/dalvikvm(731): GC freed 13038 objects / 469456 bytes in 119ms

09-21 18:38:37.588: DEBUG/dalvikvm(613): GC freed 354 objects / 14496 bytes in 152ms

09-21 18:38:38.299: DEBUG/dalvikvm(731): GC freed 14600 objects / 538008 bytes in 118ms

09-21 18:38:39.609: DEBUG/dalvikvm(731): GC freed 14326 objects / 530264 bytes in 108ms

09-21 18:38:39.829: DEBUG/dalvikvm(731): GC freed 1530 objects / 58360 bytes in 79ms

09-21 18:38:39.839: INFO/dalvikvm-heap(731): Grow heap (frag case) to 3.486MB for 4852-byte allocation

09-21 18:38:39.919: DEBUG/dalvikvm(731): GC freed 28 objects / 1168 bytes in 80ms

09-21 18:38:40.009: DEBUG/dalvikvm(731): GC freed 0 objects / 0 bytes in 88ms

09-21 18:38:40.019: INFO/dalvikvm-heap(731): Grow heap (frag case) to 3.558MB for 19360-byte allocation

09-21 18:38:40.119: DEBUG/dalvikvm(731): GC freed 0 objects / 0 bytes in 95ms

09-21 18:42:37.768: INFO/System.out(731): On a cree le vaisseau

09-21 18:42:37.868: DEBUG/dalvikvm(731): GC freed 8276 objects / 295512 bytes in 95ms

09-21 18:42:38.688: INFO/System.out(731): On ajoute un bouton

09-21 18:42:38.698: INFO/System.out(731): On ajoute un bouton

09-21 18:42:38.698: INFO/System.out(731): On ajoute un Texte Statique

09-21 18:42:38.698: INFO/System.out(731): On ajoute un texte statique

Le chargement de ce seul objet prend plus de 5 minutes (sur l'émulateur, je n'ai pas testé sur mon téléphone), ce qui n'est pas folichon :D

Comme on peut le constater, l'appli continu son bonhomme de chemin puisque j'ai mon message comme quoi l'objet a bien été créé et est en mémoire.

Et voila, magie du direct, cette fois ci l'appli a continu, tout est chargé et affiché...

Alors que dans mes précédents essais, l'appli plantait :o

Est ce que quelqu'un peut m'en apprendre plus sur la gestion mémoire d'Android?

Essayer de m'éclairer sur le résultat aléatoire du chargement de mon gros objet?

Je tiens à signaler que niveau performance, elles sont identiques à l'affichage qu'avec mon objet de 200 faces, seul le chargement laisse à désirer.

Merci de votre aide :)

edit : Je viens d'installer l'appli sur mon Hero.

Le chargement de mon objet (et sa texture) prend toujours une ou deux minutes.

L'affichage est un peu plus lent qu'avec mon ancien objet mais cela peut aussi venir de mon HUD qui commence à prendre forme (deux boutons sont affichés et (mal) texturés).

Pour ceux qui sont curieux :

http://www.marion-maxime.com/Divers/Op.mp4 << anciens objets (environ 400 faces, tous objets confondus)

http://www.marion-maxime.com/Divers/Op2.mp4 << nouvel objet (environ 800 faces)

Et si ça vous tente, l'apk permettant d'installer mon début d'application se trouve ici :

http://www.marion-maxime.com/Divers/TestOpenGL.apk (uniquement testée sous HTC Hero)

Modifié par The_Moye
Lien vers le commentaire
Partager sur d’autres sites

Héhé, merci :)

@ verbalinsurection :

Pour le moment j'ouvre mes objets en "dur", en les plaçant dans le dossier "raw" de mon appli.

Je ne peux donc pas ouvrir tout objet en dehors de mon loader du style monobj.open("objetaouvrir.obj")

Je n'ai pas encore chercher sur la technique à employer sous Android pour ouvrir un fichier en dehors du dossier raw, à partir d'une String par exemple (mais ça sera fait!).

Suite à mon post, j'ai tâché de voir où mon loader commençait à être long.

Il pêche au niveau de la duplication de sommets.

Je m'explique :

Dans le format .obj, on a une liste de tous les sommets de notre objet.

Mais certain de ces sommets sont utilisés plusieurs fois, avec des normales différentes.

Il faut donc les dupliquer, et c'est ici que je suis leeeeent, vraiment lent .

La technique que j'ai employée est en cause, peut être existe il une façon plus optimisée de faire ça?

Voici comment je procède :

_Pour chaque sommet de mon tableau de sommets chargés depuis mon fichier
   _Pour chaque normale de mon tableau de normales
       _Je parcours mon tableau d'indices à la recherche du même sommet avec une normale différente
       _S'il y a effectivement plusieurs normales pour ce sommet, je duplique.

Soit 3 boucles imbriquées, qui peuvent être très très longues suivant la complexité de l'objet (ici par exemple 397 sommets / 271 normales).

Une idée pour rendre cette étape moins fastidieuse?

Modifié par The_Moye
Lien vers le commentaire
Partager sur d’autres sites

simplifier les objets ^^

à mon avis la complexité des objets doit être revue à la baisse pour une appli mobile.

ya un tuto pour la 3D sur light racer 3D..le gars a fait un blog au jour le jour:

http://www.rbgrn.net/content/215-light-racer-3d-development-journal

la partie open gl:

http://www.rbgrn.net/content/313-light-racer-3d-days-1-2-learning-opengl-es

le gars a mis le code de son import de .obj.

Modifié par popolbx
Lien vers le commentaire
Partager sur d’autres sites

Merci pour vos réponses !

Je ne pense pas que la complexité de l'objet soit en cause... 800faces pour un objet unique ne devrait pas poser de problèmes, même pour une appli mobile.

Pour preuve, je n'ai aucun soucis lors de l'affichage, le framerate est tout a fait correcte.

Seul le chargement est long.

Une technique serait de sortir un .obj non compressé (tous les sommets sont dupliqués lors de l'exportation), mais la taille de l'appli grimperait en flèche...

@ Fluckysan

Je ne fabrique pas de second objet, juste un 2eme tableau de sommets (qui contient les coordonnées de tous les sommets de mon objets, certains étant présent plusieurs fois puisque possédants des normales différentes) à partir du premier tableau de sommet créé à partir du contenu de mon fichier.

Ces étapes (lecture du fichier, création d'un tableau contenant les coordonnées de sommets du fichiers) sont très rapides.

Seule l'étape de duplication prend un temps fou.

Je ne peux pas cloner mon tableau originel puisque le second tableau sera dans un ordre totalement différent du premier (à cause des indices de mes faces pour créer mon objet).

J'ai donc créer un nouveau tableau vide, de la taille de mon précédent tableau + les sommets dupliqués.

Et je remplis ce nouveau tableau en comparant les normales pour chaque sommets.

@popolbx

Merci pour les liens, je vais aller étudier ça de près :)

Lien vers le commentaire
Partager sur d’autres sites

Yep, ça marche !

Voici la partie qui pose problème :

       TabIndicesFacesFinal = new short[NbFacesIndices];
       TabSommets = new float[NbFacesIndices*3];
       TabNormalesFinal = new float[NbFacesIndices*3];
       TabCoordTexFinal = new float[NbFacesIndices*2];

       int IndexFacesIndice = 0;

       int Place = 0;
       int NumNormale = 0;

while(IndexFacesIndice        {
           for(int ii = 0; ii < NbNormales ; ii++)
           {
               int IndexNormalesIndice = 0;

               boolean NbNormalesTrouvees = false;

               while(IndexNormalesIndice                {
                   if(TabIndicesFaces[indexNormalesIndice] == IndexFacesIndice && TabIndicesNorm[indexNormalesIndice] == ii)
                   {
                       TabSommets[(Place*3)] = TabVertex[((TabIndicesFaces[indexNormalesIndice])*3)];
                       TabSommets[(Place*3)+1] = TabVertex[((TabIndicesFaces[indexNormalesIndice])*3)+1];
                       TabSommets[(Place*3)+2] = TabVertex[((TabIndicesFaces[indexNormalesIndice])*3)+2];

                       TabNormalesFinal[(Place*3)] = TabNormales[((TabIndicesNorm[indexNormalesIndice])*3)];
                       TabNormalesFinal[(Place*3)+1] = TabNormales[((TabIndicesNorm[indexNormalesIndice])*3)+1];
                       TabNormalesFinal[(Place*3)+2] = TabNormales[((TabIndicesNorm[indexNormalesIndice])*3)+2];

                       TabCoordTexFinal[(Place*2)] = TabCoordTex[((TabIndicesCoordText[indexNormalesIndice])*2)];
                       TabCoordTexFinal[(Place*2)+1] = TabCoordTex[((TabIndicesCoordText[indexNormalesIndice])*2)+1];

                       String tmpFI = String.valueOf(Place);
                       TabIndicesFacesFinal[indexNormalesIndice] = Short.parseShort(tmpFI);

                       NbNormalesTrouvees = true;
                   }
                   IndexNormalesIndice++;
               }
               if(NbNormalesTrouvees)
               {
                   NumNormale++;
               }
               Place = Place + NumNormale;
               NumNormale = 0;
           }

           IndexFacesIndice++;
       }

Sur mon Hero, il faut bien 2 minutes au programme pour faire le tour de cette boucle.

A la sortie, j'obtiens un tableau de sommets, un tableau de normales, un tableau de coordonnées de textures ainsi qu'un tableau d'indices me permettant de construire mon objet grâce au VBO d'OpenGL.

Merci de ton aide scanab.

Lien vers le commentaire
Partager sur d’autres sites

Est-ce que le plus simple dans ton cas ne serait pas de faire ce genre d'opération sur un bon vieux PC ?

Comme ca tu fournis à ton appli un fichier à un format largement plus digeste, et les choses se passent mieux !

Même le faire en Java sur PC apportera un boost suffisant...

Emmanuel

Lien vers le commentaire
Partager sur d’autres sites

J'y avais songé Alocaly, seulement l'objet non compressé serait alors beaucoup plus lourd.

Le modèle présenté ici pèse déjà 56Ko.

De plus, le problème serait certainement non pas écarté mais simplement déplacé :

Qui dit fichier plus lourd dit plus de temps pour le lire.

Je pense qu'il est préférable de chercher une astuce au niveau de ma façon de chercher les sommets utilisés plusieurs fois avec des normales différentes (si c'est possible).

N'étant pas un Dieu de l'optimisation, je pense qu'on pourra me conseiller :)

Lien vers le commentaire
Partager sur d’autres sites

J'y connais rien en 3D, mais :

- dans le if(TabIndicesFaces[indexNormalesIndice

ajouter un : final int toto = Place*3 et utiliser toto dans les réf. des tableaux.

ajouter un : final int titi = ((TabIndicesFaces[indexNormalesIndice])*3) et utiliser titi...

pareil pour (TabIndicesCoordText[indexNormalesIndice])*2)

et Place*2

Si je ne me trompe : les * çà prend du temps CPU.... non ?

- également çà peut pas faire de mal de faire :

TabIndicesFacesFinal[indexNormalesIndice] = Short.parseShort(String.valueOf(Place));

une référence de moins à créer, c'est mieux que rien

- éventuellement (attention à ce que le résultat reste le même)

for(int ii = 0; ii < NbNormales ; ii++)

pourrait devenir

for(int ii = NbNormales-1; i>=0 ; ii--)

c'est plus rapide de comparer un chiffre avec 0 qu'avec un autre chiffre (code Java pour çà, et même pour le CPU + rapide)

Je sais : c'est de optimisations pas trés fines, mais simples à tester

Modifié par bsegonnes
Lien vers le commentaire
Partager sur d’autres sites

Je n'y connais pas grand chose en 3D, mais ton code ne me parait pas optimal déjà au vu du nombre d'objet résultant du chargement de ton fichier... Pourrais-tu poster un fichier simple (une dizaine de ligne) d'entrée avec son équivalent de ce que tu veux avoir en sortie ?

Lien vers le commentaire
Partager sur d’autres sites

A ma connaissance il s'agit de la façon la plus commune de travailler avec un objet 3D en OpenGL.

J'ai obligatoirement besoin de tout ça :

_Un tableau d'indices pour créer mes faces en fonction du tableau de vertexs (sommets),

_Un tableau de vertexs contenant les coordonnées de tous les sommets de mon objet,

_Un tableau de coordonnées de textures,

_Un tableau de coordonnées de normales.

_Sachant que j'ai un second tableau d'indice, me permettant d'afficher mon objet en filaire.

Le plus simple serait deux triangles perpendiculaires, ayant un coté en commun :

Soit les triangles ABC et ABD.

Sommets :

A : 0 / 0 / 0

B : 1 / 0 / 0

C : 0 / 1 / 0

D : 0 / 0 / 1

Normales :

N1 : 0 / 0 / 1

N2 : 0 / 1 / 0

Faces :

F1 : A / B / C

F2 : A / B / D

On remarque tout de suite que les sommets A et B sont utilisés pour afficher les deux triangles.

A et B ont à la fois la normale N1 et la normale N2.

Je dois donc dupliquer ces sommets, j'ai donc en sortie :

Sommets :

A : 0 / 0 / 0

B : 1 / 0 / 0

C : 0 / 1 / 0

D : 0 / 0 / 0

E : 1 / 0 / 0

F : 0 / 0 / 1

Faces :

F1 : A / B / C

F2 : D / E / F

Normales :

N1 : 0 / 0 / 1

N2 : 0 / 0 / 1

N3 : 0 / 0 / 1

N4 : 0 / 1 / 0

N5 : 0 / 1 / 0

N6 : 0 / 1 / 0

J'espere que c'est plus clair , sinon n'hésite pas à me réclamer des détails :)

edit : dans l'exemple du dessus, je ne tiens pas compte des coordonnées de texture, mais le principe est le même : il faut remplir mon tableau de coordonnées de textures de façon à ce qu'il soit accessible par les mêmes indices que pour créer mes faces. C'est comme ça qu'OpenGL fonctionne.

Modifié par The_Moye
Lien vers le commentaire
Partager sur d’autres sites

J'ai lu rapidos son code, il semblerait qu'il ne tienne pas compte des normales, ce qui expliquerait qu'il n'ai pas besoin de dupliquer les sommets ayant plusieurs normales différentes.

De plus, je n'ai pas trouvé le .obj est ce qu'il le fournit?

Comme l'a précisé Alocaly plus haut, sont .obj est peut être pré-formaté pour ne pas avoir besoin de dupliquer les sommets utilisés plusieurs fois.

Après je regarderais ça plus en details quand j'aurais un peu de temps.

Ca me sera certainement utile d'ailleurs, merci pour les liens.

Modifié par The_Moye
Lien vers le commentaire
Partager sur d’autres sites

Si je comprend bien (je n'en suis pas vraiment sur :/ ), pour reprendre ton exemple, il faut que j'initialise les variables de cette façon :

NbFacesIndices = 2;

NbVertex = 4;

NbNormales = 2;

TabIndicesFaces = {0, 3, 6, 0, 3, 6};

TabIndicesNorm = {0, 3};

TabVertex = {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};

TabNormales = {0, 0, 1, 0, 1, 0};

Et je doit obtenir en sortie :

TabIndicesFacesFinal == {0, 3, 6, 9, 12, 15}

TabSommets == {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}

TabNormalesFinal == {0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0}

Je doit me planter quelque part, car je n'obtient pas ça avec ton algo.... Désolé pour mon incompétence en 3D :rolleyes:

Modifié par scanab
Lien vers le commentaire
Partager sur d’autres sites

Je pense que tu as tout bon !

Il est possible que mon algo ne donne pas exactement la même chose (je pense par exemple à l'ordre des indices), dans mon exemple ci dessus je t'ai donné l'explication générale (que tu sembles voir bien compris, chapeau !) sans la soumettre à mon algo.

Par exemple il me semble que mes indices ne tiennent pas compte du nombre de coordonnées par sommet (X,Y,Z), et que le résultat est multiplié par 3 dans ma boucle d'affichage, j'ai donc :

TabIndicesFacesFinal == {0, 1, 2, 3, 4, 5} là ou tu obtiens TabIndicesFacesFinal == {0, 3, 6, 9, 12, 15}.

OpenGL se débrouille tout seul avec ça, en lui spécifiant lors de l'affichage que chaque vertex à 3 coordonnées.

Lien vers le commentaire
Partager sur d’autres sites

En fait, je pense que j'avais tout faux... J'avais pas compris la notion d'indice... En fait, l'initialisation serai plutôt :

NbFacesIndices = 6;

NbVertex = 4;

NbNormales = 2;

TabIndicesFaces = {0, 1, 2, 0, 1, 3};

TabIndicesNorm = {0, 0, 0, 1, 1, 1};

TabVertex = {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1};

TabNormales = {0, 0, 1, 0, 1, 0};

Ce qui me donne apres exécution de ton algo :

Sommets :

avant : 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0

après : 0.0 / 0.0 / 0.0 / 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0

Normales :

avant : 0.0 / 0.0 / 1.0 / 0.0 / 1.0 / 0.0

après : 0.0 / 0.0 / 1.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 1.0 / 0.0 / 0.0 / 0.0 / 1.0 / 0.0 / 1.0 / 0.0

Indice faces :

avant : 0 / 1 / 2 / 0 / 1 / 3

après : 0 / 2 / 4 / 1 / 3 / 5

Si tu me confirme ça, je me lance dans l'optimisation...

Edit : c'est effectivement ce que tu me dit dans ton post précédent ... ;)

Modifié par scanab
Lien vers le commentaire
Partager sur d’autres sites

Si j'ai bien compris la façon dont fonctionne un .obj compressé, et à quoi il doit ressembler décompressé, le code suivant devrait mieux fonctionner :

       TabIndicesFacesFinal = new short[NbFacesIndices];
       TabSommets = new float[NbFacesIndices * 3];
       TabNormalesFinal = new float[NbFacesIndices * 3];

       short indiceSommetsFinal = 0;
       short indiceNormalesFinal = 0;
       short indiceFacesFinal = 0;
       Map mapFaces = new HashMap();
       Map mapNormales = new HashMap();
       for (int j = 0; j < TabIndicesFaces.length; j++) {
           TabIndicesFacesFinal[j] = indiceFacesFinal++;

           short numFace = TabIndicesFaces[j];
           float[] face = (float[]) mapFaces.get(new Short(numFace));
           if (face == null) {
               face = new float[3];
               face[0] = TabVertex[numFace * 3];
               face[1] = TabVertex[numFace * 3 + 1];
               face[2] = TabVertex[numFace * 3 + 2];
               mapFaces.put(new Short(numFace), face);
           }
           TabSommets[indiceSommetsFinal++] = face[0];
           TabSommets[indiceSommetsFinal++] = face[1];
           TabSommets[indiceSommetsFinal++] = face[2];

           short numNormale = TabIndicesNorm[j];
           float[] normale = (float[]) mapNormales.get(new Short(numNormale));
           if (normale == null) {
               normale = new float[3];
               normale[0] = TabNormales[numNormale * 3];
               normale[1] = TabNormales[numNormale * 3 + 1];
               normale[2] = TabNormales[numNormale * 3 + 2];
               mapNormales.put(new Short(numNormale), normale);
           }
           TabNormalesFinal[indiceNormalesFinal++] = normale[0];
           TabNormalesFinal[indiceNormalesFinal++] = normale[1];
           TabNormalesFinal[indiceNormalesFinal++] = normale[2];
       }
       mapFaces = null;
       mapNormales = null;

Tiens moi au courant :)

En fait, ce code devrait prendre moins de temps processeur, mais consommer un peu plus de mémoire... Ne sachant pas comment la mémoire est gérée dans la JVM d'Android, je ne sais pas si ça va passer...

Modifié par scanab
Lien vers le commentaire
Partager sur d’autres sites

Yop !

Alors, pour répondre à ta question scanab, il se peut effectivement qu'un sommet soit utilisé plusieurs fois avec la même normale (par exemple pour le centre des "couvercles" d'un cylindre).

J'avoue que je ne connaissais pas du tout les Map en java, et c'est magique !

Je te tire mon chapeau, ça marche du tonnerre, là ou mon code tout moche mettait 5 minutes sur l'émulateur à afficher mon objet, il n'a fallut que quelques secondes avec ta méthode :D

Fini les testes multiples, une simple recherche de clef et c'est dans la poche.

Un grand merci scanab.

Modifié par The_Moye
Lien vers le commentaire
Partager sur d’autres sites

Rejoignez la conversation

Vous pouvez poster maintenant et vous enregistrez plus tard. Si vous avez un compte, connectez-vous maintenant pour poster.

Invité
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Répondre à ce sujet…

×   Collé en tant que texte enrichi.   Coller en tant que texte brut à la place

  Seulement 75 émoticônes maximum sont autorisées.

×   Votre lien a été automatiquement intégré.   Afficher plutôt comme un lien

×   Votre contenu précédent a été rétabli.   Vider l’éditeur

×   Vous ne pouvez pas directement coller des images. Envoyez-les depuis votre ordinateur ou insérez-les depuis une URL.

×
×
  • Créer...