Frames per Second



  • Hey,

    Ich habe mein kleines Level fertig geschrieben via SDL2 und C++. Mir fällt nur auf, dass bei manchen Szenenbildern der Bildschirm nicht ganz hinterherkommt.

    Es gibt eine Funktion interact(), die im game loop berechnungen durchführt. Wenn sie was neues berechnet habt (aufgrund eines bestimmten Zeitpunkt beispielsweise, oder einer Interaktion), gibt sie TRUE zurück.

    Wenn interact() also TRUE ist, nur dann wird draw() aufgerufen.

    Beim Ausgeben von FPS zeichnet er im besagten Szenenbild bis zu 130 mal, was erklären würde, wieso er nicht hinterherkommt (ist vielleicht zu schnell).

    Jetzt meine Frage:

    Angenommen, ich will das auf meinetwegen 50 fps begrenzen, wie läuft das? Lege ich da am Anfang bei Zeitpunkt 0 einen Startzeitpunkt fest und zeichne alle 20 ms? (natürlich nur, wenn er muss). Weil ich hab das mit der eigenen Maus ingame testweise mal gemacht. Die wird dadurch auch nicht sauberer gerendert.



  • Man begrenzt ein Spiel auch nicht auf 50fps, man begrenzt es und synchronisiert es mit der Bildausgabe. SDL_RENDERER_PRESENTVSYNC



  • Man rendert auch nicht nach jedem Logik-Schritt, man rendert einfach durchgängig und sagt der Logik wie viel Zeit vergangen ist. 😉



  • Tja, wenn man keine Ahnung hat und alles ausm Bauch heraus macht, kommt sowas zu Stande 😃 Auf die Idee mit der Zeit bin ich so gar nicht gekommen.

    Heißt, wenn die draw Funktion mal länger braucht, dann berechnet die logik funktion einfach mehr Schritte, sodass beim nächsten sichtbaren Frame nen größerer Sprung sichtbar ist?

    Wie setzt man das am klügsten um? Angenommen ich möchte einen Pixel 100px / Sekunde in eine Richtung bewegen. Beim ersten Schleifendurchlauf hat er meinetwegen 3ms gebraucht. Wäre das dann:

    position += (0.003 * 100);
    

    ?

    In Bezug auf den SDL_Renderer. Ich nutze momentan den SDL_RENDERER_ACCELERATED. Man kann die zwar zusammen "OR"'den, aber was genau tut SDL_RENDERER_PRESENTVSYNC? In der Wiki steht nur "present is synchronized with the refresh rate". Die refresh rate, die ich setze? Oder gibts da intern ne Funktion zu?



  • edit: Hatte Unfug geschrieben. Ignoriert diesen Post einfach. 😉



  • Wenn du eine Nvidia Karte hast kannst du VSync relativ einfach in diesem Control-Center einschalten. Das begrenzt deine Frames auf 60/s begrenzen. Bei ATI-Karten dürfte es auch nicht viel schwerer sein. Falls du erst mal eine schnelle Lösung suchst.



  • vsync schrieb:

    Wenn du eine Nvidia Karte hast kannst du VSync relativ einfach in diesem Control-Center einschalten. Das begrenzt deine Frames auf 60/s begrenzen. Bei ATI-Karten dürfte es auch nicht viel schwerer sein. Falls du erst mal eine schnelle Lösung suchst.

    Nicht auf 60, sondern auf die Refresh-Rate deines Monitors. Bei mir waren das immer 60Hertz 😛



  • PadMad schrieb:

    Wie setzt man das am klügsten um?

    Entweder indem du die Geschwindigkeit mit der Zeit multiplizierst, oder, wenn du fixe Logik-Intervalle haben willst (das kann von Vorteil sein, wenn man immer die exakt gleichen Ergebnisse haben will), dann "sammelst" du die Zeit und wenn du genug hast, machst du einen Logik-Schritt. (Oder mehrere.)



  • Das sind alles sehr hilfreiche Tipps. Ich denke ich werde mich für die Variante mit der Zeit, die mulitpliziert wird entscheiden. Mir fehlt momentan nur die Idee für die Mausbewegung. Weil ein Mauszeiger hat sogesehen ja keine Geschwindigkeit.

    Da wird ja, denke ich, neugezeichnet, wenn sie ihre Koordinaten verändert.



  • Mauszeiger würde ich entweder das System zeichnen lassen, oder in einem separaten Thread behandeln.



  • cooky451 schrieb:

    Mauszeiger würde ich entweder das System zeichnen lassen, oder in einem separaten Thread behandeln.

    Und was soll dieser Thread machen?





  • Ein guter Beitrag. (der davor)

    Ich habe versucht das jetzt umzusetzen. Angefangen habe ich beim Problem mit der Maus. Ich habe einen eigenen Mauszeiger erstellt und möchte den, je nach Bewegung, rendern. Mein Bildschirm ist im übrigen auf 60 Hertz eingestellt.

    Wenn ich meinen programmierten Mauszeiger auf 60 Hertz einstelle, dann sieht man wie der Bildschirm nicht hinterherkommt, da die Maus nie ganz gezeichnet wird. Bei 10 FPS kommt der Bildschirm hingegen noch mit und die Grafik wird immer ganz gezeichnet. Der SDL_Renderer wurde mit SDL_RENDERER_PRESENTVSYNC erstellt.

    C++ / SDL2

    main.cpp

    int main(int argc, char* args[])
    {
      SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER | SDL_INIT_AUDIO);
    
      CBuild * pBuild = new CBuild;
      STimeWindow * pTimeWindow = new STimeWindow;
      pTimeWindow->TimeStampA = 0;
      pTimeWindow->TimeStampB = SDL_GetTicks();
      double time_dif = 0.0;
    
      pBuild->init();
    
      if(pBuild->getWindowX() < 1024 || pBuild->getWindowY() < 768)
      {
        SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Niedrige Aufloesung", "Leider ist die Aufloesung zu niedrig.\nDie Aufloesung muss mindestens 1024 x 768\nbetragen.", NULL);
      }
      else
      {
        while(pBuild->getExecuteFlag())
        {
          pBuild->draw();
          pBuild->update(time_dif, pTimeWindow->TimeStampB);
    
          time_dif = (pTimeWindow->TimeStampB - pTimeWindow->TimeStampA);
          time_dif *= 0.001;
          pTimeWindow->TimeStampA = pTimeWindow->TimeStampB;
          pTimeWindow->TimeStampB = SDL_GetTicks();
        }
      }
    
      SDL_Quit();
    
      delete pTimeWindow;
      return 0;
    }
    

    cbuild.cpp

    ///////////////////////////////////////
    ///////////////////////////////////////
    // constructor / destructor
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    CBuild::CBuild():
    execute(1),
    fps(60)
    {
      pWindow = NULL;
      pRenderer = NULL;
      pWndSurf = NULL;
      pCursor = new CCursor();
    }
    
    CBuild::~CBuild()
    {}
    
    ///////////////////////////////////////
    ///////////////////////////////////////
    // private methods
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    void CBuild::init_window()
    {
      SDL_GetCurrentDisplayMode(0, &displaymode);
      pWindow = SDL_CreateWindow
      (
        "Test",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        displaymode.w,
        displaymode.h,
        SDL_WINDOW_SHOWN
      );
      pRenderer = SDL_CreateRenderer(pWindow, -1, SDL_RENDERER_PRESENTVSYNC);
      pWndSurf = SDL_GetWindowSurface(pWindow);
    }
    
    void CBuild::handleEvents()
    {
      while(SDL_PollEvent(&event))
      {
        switch(event.type)
        {
          case SDL_KEYDOWN:
          {
    	this->event_keydown();
          } break;
        };
      }
    }
    
    void CBuild::event_keydown()
    {
      switch(event.key.keysym.sym)
      {
        case SDLK_ESCAPE:
        {
          execute = 0;
        } break;
      };
    }
    
    ///////////////////////////////////////
    ///////////////////////////////////////
    // public methods
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    bool CBuild::getExecuteFlag()
    {
      return execute;
    }
    
    int CBuild::getWindowX()
    {
      return displaymode.w;
    }
    
    int CBuild::getWindowY()
    {
      return displaymode.h;
    }
    
    ///////////////////////////////////////
    ///////////////////////////////////////
    // main methods
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    void CBuild::init()
    {
      this->init_window();
      pCursor->initCursor("graphics/cursor.png", fps, pRenderer);
    }
    
    void CBuild::draw()
    {
      SDL_SetRenderDrawColor(pRenderer, 0x00, 0x00, 0x00, 0xff);
    
      SDL_RenderClear(pRenderer);
      pCursor->drawCursor(pRenderer);
    
      SDL_RenderPresent(pRenderer);
    }
    
    void CBuild::update(double timestep, int timestamp)
    {
      this->handleEvents();	
      pCursor->moveCursor(timestamp);
    }
    

    ccursor.cpp

    ///////////////////////////////////////
    ///////////////////////////////////////
    // constructor / destructor
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    CCursor::CCursor():
    time_move(0),
    time_step(0)
    {
      pPosition = new SDL_Rect;
      pTexture = NULL;
    }
    
    CCursor::~CCursor()
    {}
    
    ///////////////////////////////////////
    ///////////////////////////////////////
    // methods
    ///////////////////////////////////////
    ///////////////////////////////////////
    
    void CCursor::initCursor(char * pFile, int nFPS, SDL_Renderer * pRenderer)
    {
      SDL_Surface * pSurfImage = NULL;
      time_step = 1000 / nFPS;
      SDL_ShowCursor(0);
    
      pSurfImage = IMG_Load(pFile);
      pTexture = SDL_CreateTextureFromSurface(pRenderer, pSurfImage);
      pPosition->w = pSurfImage->w;
      pPosition->h = pSurfImage->h;
      SDL_FreeSurface(pSurfImage);
      SDL_SetTextureBlendMode(pTexture, SDL_BLENDMODE_BLEND);
      SDL_SetTextureAlphaMod(pTexture, 0xff);
    }
    
    void CCursor::moveCursor(int timer)
    {
      if(timer >= time_move)
      {
        SDL_GetMouseState(&pPosition->x, &pPosition->y);
        time_move += time_step;
      }
    }
    
    void CCursor::drawCursor(SDL_Renderer * pRenderer)
    {
      SDL_RenderCopy(pRenderer, pTexture, NULL, pPosition);
    }
    

    Fakt ist, dass er eigentlich 30 FPS in der CCursor Klasse schon nicht mehr hinbekommt. Der Cursor, den ich verwende, kann hier heruntergeladen werden:

    http://www.wp1105995.server-he.de/cursor.png

    Das heißt, ich muss irgendwas grundlegend mit SDL2 falsch machen.



  • Nutze am besten erst mal den Default-Cursor des Systems und mach das später, bis dein Programm ordentlich läuft.



  • Das ist ja aber 1 von 2 Hauptproblemen. Die Maus mit eigenem Cursor.

    Das 2. Hauptproblem ist, dass der Bildschirm bei Animationen ebenfalls nicht hinterherkommt. Das will ich jetzt aber durch die neue Zeitlogik mal testen. Indem ich stoppe wie lange das Programm zum Zeichnen eines Frames braucht. Das war ja vorher nicht so.


  • Mod

    spar dir viel arbeit und installiere dir einen profiler, z.b. AMDs codeanalyst und schau wo die performance verloren geht.

    bei sowas einfachem solltest du auf jeden fall 60fps hinbekommen, selbst auf einem 10jahre alten rechner.



  • Hey,

    Visual Studio 2013 liefert den sogar mit. Ich habe einen CPU Leistungstest gemacht. Der Graf kratz halt kurz über der 0% Hürde. Ich kann daraus auch keine Auffälligkeiten entnehmen.

    Das gleiche Problem habe ich aber auch auf einem anderen Gerät. Ich meine das mit dem nicht hinterherkommen beim Zeichnen, was mich nach wie vor glauben lässt, dass ich irgendwas im Code bezüglich SDL falsch mache.

    Die Anwendung:
    http://www.wp1105995.server-he.de/Beispiel.zip

    Der Leistungsbericht:
    http://www.wp1105995.server-he.de/CPU.zip



  • Lad mal das VS Projekt mit Sourcecode hoch.



  • Ich habe einfach mal den ganzen Projekt Ordner gezippt und hochgeladen. Bin gespannt, wie das bei euch läuft.

    Danke schonmal vorweg.

    http://www.wp1105995.server-he.de/project.zip



  • Ändere mal

    pRenderer = SDL_CreateRenderer(pWindow, -1, SDL_RENDERER_PRESENTVSYNC);
    

    auf

    pRenderer = SDL_CreateRenderer(pWindow, -1, SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_ACCELERATED);
    

    Macht das einen Unterschied?

    edit:
    Und warum benutzt du nicht http://wiki.libsdl.org/SDL_CreateCursor ?


Anmelden zum Antworten