Bienvenue, Invité. Merci de vous connecter ou de vous inscrire. Avez-vous oublié d'activer ?

Voir les contributions

Cette section vous permet de consulter les contributions (messages, sujets et fichiers joints) d'un utilisateur. Vous ne pourrez voir que les contributions des zones auxquelles vous avez accès.


Messages - nagimar

Pages: [1] 2 Suivante »
1
Salut à tous, ça y est j'ai enfin trouvé ou ça coïnce au niveau des performances!

https://quick-bench.com/q/jEGRWG62s39A0OzY22E-iNtuvWo

Je vais donc changer la conception du framework, il devrait être vachement plus rapide une fois cela de fait!
Je suis curieux de savoir ce que ça va donner au niveau du raytracing, qui pour l'instant, est beaucoup trop lent.

2
Discussions générales / Re: SFML est lente sur de gros projets.
« le: Juillet 23, 2021, 09:58:21 pm »
Bon, j'ai trouvé ou ça coince au niveau des performances, c'était en effet, les fonctions virtuelles, et l'héritage que j'utilisais à mauvais escient même avec le polymorphisme d'inclusion.

On peut voir ici qu'il y a une large différence de performance entre un système SFML-Like et un système ECS comme Unity que je vais utiliser dans mon framework d'ailleurs :

https://quick-bench.com/q/jEGRWG62s39A0OzY22E-iNtuvWo

3
J'ai essayé plusieurs design de conception et je me suis rendu compte que au niveau des performances, le design actuel est celui qui convient le mieux.

Par contre j'ai des pertes de performances au niveau des draws calls. Je ne sais pas trop ce que je vais faire, utiliser plusieurs threads pour éliminer les goulots d'étranglement, comme je faisais avant, je les avais retiré parce que, une personne m'avait conseillé de le faire mais j'ai remarqué que il faut pas toujours écouter ce que les gens propose, surtout sans avoir fait de tests de performance.

4
Discussions générales / Re: SFML est lente sur de gros projets.
« le: Juillet 17, 2021, 10:26:42 am »
Les pertes de performances j'ai remarqué se font surtout au niveau du draw.

Le polymorphisme d'inclusion est plus rapide que les virtuels, sauf dans le cas ou j'ai une structure arborescente! Et ce, même si j'ai une dizaine de fonctions virtuelles et une dizaines de types, il faut juste utiliser plusieurs interfaces pour ne pas avoir 100 types qui redéfinissent 100 méthodes virtuelles.

Mais je pense que pour des classes du genre RenderTarget qui on n'est sûr, ne seront pas des structures arborescentes, utiliser le polymorphisme d'inclusion est plus performant, ce qui n'est pas le cas pour Drawable et Transformable par exemple.

Par contre, un système du style ECS est plus lent, j'ai remarqué.

5
non, et puis de toute façon je possède une interface, je peux passer d'une lib à l'autre sans soucis si j'ai un problème avec SFML parce que par exemple SFML le module fenêtre de la SFML ne gère pas Vulkan, là je suis entrain de faire des tests pour voir ce qui est plus performant, je pense que un système sans polymorphisme est le plus performant car pas besoin de rechercher des types (enfin c'est ce qu'on m'a porposé de faire), cependant, pas de redéfinition de comportements possible donc plus lourd à mettre en œuvre et c'est ça qui est frustrant, qu'on utilise la POO et qu'au final on se rend compte que ...

Je vais quand même faire un benchmark pour voir si, ça vaut la peine de change le design du framework.



6
Graphique / Re: texte déformé à cause du redimensionnement
« le: Juillet 15, 2021, 01:08:37 pm »
Je crois que c'est le glViewport qui utilise la SFML qui fait ça, il faut changer l'échelle suivant le redimentionnement de la fenêtre (ou désactiver le viewport ?), mais je ne pense pas que la SFML permet de désactiver le viewport, je pense qu'il est activé d'office dans la classe rendertarget.
J'avais eu le même problème avec les interfaces graphiques et les menus qui n'étaient pas positionnés au même endroit lors du redimentionement de la fenêtre, j'ai dû donc  modifier la matrice de projection pour changer la perspective de la vue en fonction de la taille de la fenêtre. (Je fait ça dans mon framework mais je ne sais pas si la SFML permet de le faire.)

7
Graphique / Re: Changer la taille du texte d'un bouton avec SFGUI
« le: Juillet 15, 2021, 01:03:44 pm »
Je ne comprend pas ce que tu veux faire, un setScale sur le texte ne fonctionne pas ? (Dans ce cas il faut aussi changer la taille du bouton probablement)

Maintenant je n'utilise pas sfgui donc je ne sais pas.

8
Graphique / Re: Limiter les FPS
« le: Juillet 15, 2021, 01:02:55 pm »
Salut! Et ceci, ça ne fonctionne pas ?

nouvelle position = direction * tempsEcoulé;
 


9
Discussions générales / SFML est lente sur de gros projets.
« le: Juillet 15, 2021, 12:59:55 pm »
Salut, j'ai commencé à utiliser la SFML il y a bien longtemps et mon projet s'agrandissant, je me suis rendu compte que la SFML est lente et on m'a clairement dit que la SFML n'était pas une référence pour faire de gros projets avec opengl.

Les appels à glDraw sont lents surtout lorsqu'on utilise pas mal de textures de rendu, SFML n'utilises aucune technique moderne, que ça soit les buffers (UBO,  bindless textures, etc...), j'ai donc tenté de changer les classes de la SFML pour faire tout ça, mais ça reste lent, à cause des appels à glDraw à chaque rendertexture, j'ai pensé à utiliser des threads pour faire plusieurs appels à draw simultanément en espérant que ça n'altère pas le rendu final (normalement non car c'est opengl qui se charge de faire la synchronisation au niveau de la mise à jour de l'affichage) et que je n'aie pas trop de crash à cause des accès concurrents, on m'a même carrément conseillé d'utiliser VULKAN plutôt qu'opengl pour faire du multithreading, cependant, SFML ne possède actuellement pas d'implémentation pour VULKAN, je n'en ai pas trouver du moins.

Mais il n'y a pas que ça, j'ai remarqué que plus le CPU est lent à l'exécution, plus le GPU l'est aussi, j'ai remarqué que les fonctions virtuelles (SFML en utilise) sont lentes sur de plus gros projets, parce que, la recherche de la bonne fonction à appeler se fait à l'exécution et non pas en compilation.

J'ai pensé alors à un système de type ECS qui utilise la composition plutôt que l'héritage cependant cela est lourd pour gérer des noeuds (Récupérer les composants de tout les noeuds enfants et calculer la transformation finale) comme c'est illustré dans l'exemple de l'un des tutoriels de la SFML d'ailleurs.

J'ai donc pensé aux nouvelles fonctionnalités du c++ moderne c'est à dire aux templates variadiques et au polymorphisme d'inclusion qui est beaucoup plus rapide d'après mes tests (Malgré le fait qu'il faille rechercher le bon type dérivé à l'exécution mais je le fait à coup de "if" générés en compilation ce qui est rapide) et j'ai implémenté cette solution.

Pourquoi la SFML, n'utilise t'elle pas des fonctionnalités du c++ moderne ? (Et il n'y a pas que la SFML, beaucoup d'ancienne bibliothèque ne le font pas)
Est ce que cela va changer à l'avenir ?

Je pense que si les nouvelles technologies existent, c'est pour certaines raisons, des questions de performance la plupart du temps.


 

10
Salut! Je n'ai pas donné de nouvelles depuis longtemps, mais il va y avoir beaucoup de changement au niveau de ce projet, en effet.

J'ai remarqué la lenteur de la SFML et on m'a clairement fait savoir que la SFML n'était clairement pas une référence pour utiliser opengl de manière performante pour de gros projets. On m'a également dit que la création d'un contexte opengl par texture de rendu n'est pas une bonne solution qu'il valait mieux n'utiliser qu'un seul contexte. (Bien que dans un contexte multithread ou pour créer plusieurs fenêtre pas le choix, mais dans le cas du multithreading on m'a conseillé d'utiliser VULKAN)

Et je vais avoir besoin du multithreading pour faire plusieurs rendus hors écran simultanément (SFML ne le fait pas par défaut) en même temps, car, les appels à glDraw pour toutes mes textures de rendu sont lent, même si j'utilise le bindless texturing et les buffers pour ne faire qu'un seul appel à glDraw par texture de rendu.

Autre chose qui ralenti (mais côté CPU ici) ce sont les fonctions virtuelles, en effets, la recherche de la bonne fonction à appeler se fait à l'exécution et la SFML utilise des fonctions virtuelles.

J'ai alors réfléchi à une autre solution, sans pour autant supprimer l'héritage parce que j'avais pensé à un système de type ECS mais dans ce cas je devrais récupérer les composants de transformations de toutes les entités enfants, les enfants des enfants, etc... et calculer la matrice de transformation finale combinée et également les positions relatives hors que avec l'héritage je peux faire ça automatiquement avec une méthode onMove par exemple que je redéfini, comme ça, tout se remet à jour automatiquement à chaque fois que j'appelle move, sinon je devrai le faire manuellement après chaque appel à move. (SFML ne possède pas de méthode onMove)

Alors j'ai recherché une solution pour pouvoir utiliser l'héritage sans utiliser de fonctions virtuelles comme le fait la SFML, et j'ai trouvé une solution, qui utilise les templates variadique et le polymorphisme d'inclusion.

J'ai testé ça dans la fonction main la rapidité et je me suis rendu compte que c'est vachement plus rapide (4 fois plus rapide) que d'utiliser des fonctions virtuelles comme le fait la SFML, en plus j'en utilise pas mal dans ODFAEG actuellement, ce qui ralenti.

La solution que j'ai trouvée est d'inclure chaque nouveau type dans un tuple, et générer une série de fonctions qui vont se charger d'appeler un foncteur, sur le bon type. (En faisant une vérification (SFINAE) sur le nombre d'arguments et le type des arguments dans le cas d'une surcharge de fonction pour pas que le compilateur râle parce qu'il ne trouve pas la fonction lors de l'appel)

Et à l'exécution il va choisir la bonne fonction à appeler suivant le type, au moyen de if, else..., le type n'est rien d'autre que la position du type dans le "parameter pack".

Voici un code qui montre ce que ça donne au niveau de l'utilisation, j'ai testé et, ça à l'air vraiment cool, comme le type du tuple change à l'exécution je ne peux pas le stocker dans une classe en tant que variable membre, mais je peux le créer et le passer à une fonction (la boucle de jeux) ce qui m'évite de devoir utiliser un global.



Je pense également à mettre les constructeurs en privé et utiliser une factory qui va se charger de gérer les variables globale, plutôt qu'utiliser des variables statiques qui pose problème lors de l'utilisation de plugin, vu que sur windows, elles ne sont pas partagées lors de l'appel du pointeur de fonction extrait de l'une dll.

struct transformable {
    template<typename D>
    void move() {
        static_cast<D&>(*this).onMove();
    }
};

struct entity : transformable {
    unsigned int containerIdx, childrenContainerIdx, type, childrenContainerType, id;
    static unsigned int nbEntities;
    std::string managerName;
    entity() : transformable () {
        id = nbEntities;
        nbEntities++;
    }
    template <typename D, typename EntityManager>
    auto addChild(EntityManager& em, D* entity) {
        auto newem = em.add(entity);
        return newem;
    }
    template <typename EntityManagerArray, typename D, typename... Args, size_t... Ints, class = typename std::enable_if<sizeof...(Args) == 3>::type>
    void operator()(EntityManagerArray& ema, D* entity, std::string f, std::tuple<Args...> args, std::index_sequence<Ints...>) {
        if (f == "move") {
            move(ema, entity, std::get<Ints>(args)...);
        }
    }
    template <typename R, typename EntityManagerArray, typename D, typename... Args, size_t... Ints, class = typename std::enable_if<sizeof...(Args) == 3>::type>
    void operator()(EntityManagerArray& ema, D* entity, std::string f, std::tuple<Args...> args, std::index_sequence<Ints...>, R& ret) {
        if (f == "move") {
            ret = move(ema, entity, std::get<Ints>(args)...);
        }
    }
    template <typename R, typename EntityManagerArray, typename D, typename... Args, size_t... Ints, class... E, class = typename std::enable_if<sizeof...(Args) == 0>::type>
    void operator()(EntityManagerArray& ema, D* entity, std::string f, std::tuple<Args...> args, std::index_sequence<Ints...>, R& ret) {
        if (f == "f") {
            ret = func();
        }
    }
    float func() {
        return 1.1f;
    }
    template <typename EntityManagerArray, typename D>
    bool move(EntityManagerArray& ema, D* entity, float x, float y, float z) {
        transformable::move<D>();
        auto tp = std::make_tuple(x, y, z);
        ema.apply(entity->childrenContainerType, entity->childrenContainerIdx, "move", tp);
        return true;
    }
    void onMove() {
    }
};

unsigned int entity::nbEntities = 0;



struct sphere : entity
{
  sphere() : entity() {

  }
  void onMove() {
      std::cout<<"update datas on sphere instance : "<<id<<" moved"<<std::endl;
  }

};
struct rectangle : entity
{
  rectangle() : entity() {

  }
  void onMove() {
      std::cout<<"update datas on rectangle instance : "<<entity::id<<" moved"<<std::endl;
  }
};
struct convexshape : entity
{
    convexshape() : entity() {

    }
    void onMove() {
        std::cout<<"update datas on convex shape instance : "<<entity::id<<" shape"<<std::endl;
    }
};
template <typename D>
struct appli {
    bool running = false;
    bool isRunning () {
        return running;
    }
    template<typename EntityManagerArray>
    void update(EntityManagerArray em) {

    }
    template<typename EntityManagerArray>
    void init(EntityManagerArray& em) {
        static_cast<D*>(this)->onInit(em);
    }
    template<typename EntityManagerArray>
    void exec(EntityManagerArray& ema) {
        if (!running) {
            running = true;
            init(ema);
        }
        while(running) {

            static_cast<D*>(this)->onExec(ema);
        }
    }

};
struct myappli : appli<myappli> {
    sphere sph;
    rectangle rect, rect2;
    convexshape shape, shape2;
    myappli() : appli<myappli>() {

    }
    template<typename ManagerArrayEntity>
    void onInit(ManagerArrayEntity& ema) {
        sph.managerName="entity managers";
        rect.managerName="entity managers";
        shape.managerName="entity managers";

        ::EntityManager<IEntityManager, entity> ems;
        //ems.name = "entity managers";
        ::EntityManager<IEntityManager, entity> childrenem;
        //childrenem.name = "sphere children manager";

        auto children0em = sph.addChild(childrenem, &rect2);
        auto children1em = sph.addChild(children0em, &shape2);
        children1em.name = "sphere children";
        auto em = ems.add(&shape).add(&rect).add(&sph);
        em.name = "entity managers";
        auto emas = ema.add(&em).add(&children1em);
        sph.childrenContainerIdx = children1em.containerIdx;
        sph.childrenContainerType = children1em.type;
        exec(emas);
    }
    template<typename ManagerEntityArray>
    void onExec(ManagerEntityArray& ema) {
        bool retb;
        auto tp = std::make_tuple(1.1f, 2.2f, 3.3f);
        ema.apply(0, 0, sph.type, sph.containerIdx, "move", tp, retb);
        float retf;
        auto tp2 = std::make_tuple();
        ema.apply(0, 0, sph.type, sph.containerIdx, "f", tp2, retf);
        std::cout<<"rets : "<<retb<<","<<retf<<std::endl;
        ema.apply(0, 0, rect.type, rect.containerIdx, "move", tp, retb);
        ema.apply(0, 0, shape.type, shape.containerIdx, "move", tp, retb);

    }
};

int main(int argc, char* argv[])
{    
    myappli appli;
    EntityManagerArray<IEntityManager> entityManagerArray;
    appli.exec(entityManagerArray);

 

De gros changements sont à venir donc..., je ne sais pas si ODFAEG va rester un projet SFML, mais en tout cas le design de la SFML va être remplacé. (Enfin tout dépendra de la rapidité de ce nouveau design à l'exécution lorsque je l'implémenterai dans le framework et surtout, si ça fonctionne pour tout mes projets utilisant le framework.)








12
Suggestions de nouvelles fonctionnalités / Re: un support pour Wayland
« le: Janvier 18, 2021, 08:03:01 pm »
Je crois que SFML va disparaitre avec le temps, pas de support pour les nouvelles technologies.

13
Salut, je ne sais pas si opengl va persister on m'a conseillé de passer à vulkan mais je la trouve, comment dire, pas du tout adaptée pour SFML déjà que SFML utilise les anciennes versions de opengl et que vulkan fonctionne à peut prêt comme l'opengl moderne, en plus le module fenêtre de la SFML crée un contexte opengl je ne pense pas qu'on puisse créer une fenêtre SFML sans contexte opengl et utiliser vulkan comme on pourrait le faire avec la SDL et glfw.

Et si opengl disparait SFML disparait aussi à moins de la recoder pour vulkan et j'aurai bien aimé avoir un code source de référence pour encapsuler vulkan dans des classes textures, shader, rendertexture comme le fait la SFML histoire d'être sûr de faire ça correctement pour mon framework.



14
Système / undefined references errors.
« le: Janvier 12, 2021, 12:02:33 am »
Salut! je voudrais essayer de compiler une lib en shared qui utilise SFML mais j'ai des messages d'erreurs :
CMakeFiles\odfaeg-math.dir/objects.a(distribution.cpp.obj):distribution.cpp:(.text+0x15d): undefined reference to `_imp___ZN2sf7secondsEf'
CMakeFiles\odfaeg-math.dir/objects.a(distribution.cpp.obj):distribution.cpp:(.text+0x350): undefined reference to `_imp___ZN2sf5ColorC1Ev'

CMakeFiles\odfaeg-math.dir/objects.a(distribution.cpp.obj):distribution.cpp:(.text+0x917): undefined reference to `_imp___ZNK2sf4Time9asSecondsEv'
C:/PROGRA~2/CODEBL~1/MinGW/bin/../lib/gcc/mingw32/5.1.0/../../../../mingw32/bin/ld.exe: CMakeFiles\odfaeg-math.dir/objects.a(distribution.cpp.obj): bad reloc address 0x4 in section `.text.startup'

collect2.exe: error: ld returned 1 exit status
src\odfaeg\Math\CMakeFiles\odfaeg-math.dir\build.make:246: recipe for target 'lib/odfaeg-math-1.dll' failed
mingw32-make[2]: *** [lib/odfaeg-math-1.dll] Error 1
CMakeFiles\Makefile2:263: recipe for target 'src/odfaeg/Math/CMakeFiles/odfaeg-math.dir/all' failed
mingw32-make[1]: *** [src/odfaeg/Math/CMakeFiles/odfaeg-math.dir/all] Error 2
Makefile:128: recipe for target 'all' failed
mingw32-make: *** [all] Error 2
 

J'ai des undefined references de sf::seconds, sf::Color et sf::Time :


/////////////////////////////////////////////////////////////////////////////////
//
// Thor C++ Library
// Copyright (c) 2011-2014 Jan Haller
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////

#include "../../../include/odfaeg/Math/distributions.h"

#include <cassert>


namespace odfaeg
{
    namespace math {
        namespace Distributions
        {
            namespace
            {
                template <typename T>
                Distribution<T> uniformT(T min, T max)
                {
                    assert(min <= max);

                    return [=] () -> T
                    {
                        return Math::random(min, max);
                    };
                }
            }
            // ---------------------------------------------------------------------------------------------------------------------------


            Distribution<int> uniformi(int min, int max)
            {
                return uniformT(min, max);
            }

            Distribution<unsigned int> uniformui(unsigned int min, unsigned int max)
            {
                return uniformT(min, max);
            }

            Distribution<float> uniform(float min, float max)
            {
                return uniformT(min, max);
            }

            Distribution<sf::Time> uniform(sf::Time min, sf::Time max)
            {
                assert(min <= max);

                const float floatMin = min.asSeconds();
                const float floatMax = max.asSeconds();

                return [=] () -> sf::Time
                {
                    return sf::seconds(Math::random(floatMin, floatMax));
                };
            }

            Distribution<sf::Vector3f> rect(sf::Vector3f center, sf::Vector3f halfSize)
            {
                assert(halfSize.x >= 0.f && halfSize.y >= 0.f);

                return [=] () -> sf::Vector3f
                {
                    return sf::Vector3f(
                    Math::random(center.x - halfSize.x, center.x + halfSize.x),
                    Math::random(center.y - halfSize.y, center.y + halfSize.y),
                    Math::random(center.z - halfSize.z, center.z + halfSize.z));
                };
            }

            Distribution<sf::Vector3f> circle(sf::Vector3f center, float radius)
            {
                assert(radius >= 0.f);

                return [=] () -> sf::Vector3f
                {
                    //sf::Vector3f radiusVector = sf::Vector3f(radius * std::sqrt(Math::random(0.f, 1.f)), Math::random(0.f, 360.f), 0);
                    sf::Vector3f radiusVector = sf::Vector3f(Math::random(0, radius) * Math::cosinus(Math::random(0, Math::toRadians(360))), Math::random(0, radius) * Math::sinus(Math::random(0, Math::toRadians(360))), 0);
                    return center + radiusVector;
                };
            }

            Distribution<sf::Vector3f> deflect(sf::Vector3f direction, float maxRotation)
            {
                return [=] () -> sf::Vector3f
                {
                    Vec3f v(direction.x, direction.y, direction.z);
                    graphic::TransformMatrix tm;
                    tm.setRotation(Vec3f(0, 0, 1), Math::random(0.f - maxRotation, 0.f + maxRotation));
                    Vec3f t = tm.transform(v);
                    return sf::Vector3f(t.x, t.y, t.z);
                };

            }
            Distribution<sf::Color> color(Vec3f color1, Vec3f color2) {
                return [=] () -> sf::Color {
                    sf::Color color;
                    color.r = Math::clamp(Math::random(color1.x, color2.x), 0, 255);
                    color.g = Math::clamp(Math::random(color1.y, color2.y), 0, 255);
                    color.b = Math::clamp(Math::random(color1.z, color2.z), 0, 255);
                    color.a = Math::clamp(Math::random(color1.w, color2.w), 0, 255);
                    return color;
                };
            }
        }

    } // namespace Distributions
} // namespace thor

 

Je lie SFML dans le fichier CMake :
# include the ODFAEG specific macros
include(${PROJECT_SOURCE_DIR}/cmake/Macros.cmake)

set(INCROOT ${PROJECT_SOURCE_DIR}/include/odfaeg/Math)
set(SRCROOT ${PROJECT_SOURCE_DIR}/src/odfaeg/Math)
set(SRC
        ${INCROOT}/transformMatrix.h
        ${SRCROOT}/transformMatrix.cpp
        ${INCROOT}/export.hpp
        ${INCROOT}/computer.h
        ${SRCROOT}/computer.cpp
        ${INCROOT}/maths.h
        ${SRCROOT}/maths.cpp
        ${INCROOT}/matrix2.h
        ${SRCROOT}/matrix2.cpp
        ${INCROOT}/matrix3.h
        ${SRCROOT}/matrix3.cpp
        ${INCROOT}/matrix4.h
        ${SRCROOT}/matrix4.cpp 
        ${INCROOT}/vec2f.h
        ${SRCROOT}/vec2.cpp
        ${INCROOT}/vec4.h
        ${SRCROOT}/vec4.cpp
        ${INCROOT}/distributions.h
        ${INCROOT}/distribution.h
        ${SRCROOT}/distribution.cpp
        ${INCROOT}/ray.h
        ${SRCROOT}/ray.cpp
        ${INCROOT}/bigInt.hpp
        ${SRCROOT}/bigInt.cpp)
       
include_directories(${CMAKE_INCLUDE_PATH})
link_directories (${CMAKE_LIBRARY_PATH})
find_package (SFML 2 REQUIRED)
sfgl_add_library(odfaeg-math
                                 SOURCES ${SRC}
                                 DEPENDS odfaeg-core)
target_link_libraries (odfaeg-math ${SFML_LIBRARIES})
 

15
Graphique / Re: Quelquee questions sur sf::Text
« le: Décembre 11, 2020, 08:23:21 pm »
Bon, j'ai changé la classe sf::Text moi même pour mettre une couleur de fond.

Pages: [1] 2 Suivante »