2016-10-02 16 views
-1

SDL 2.0.4とGLEW 1.13.0を使用していますが、SDL2にはある種のfps制限があるようです。私はこれを知っています、私はある時間測定をしたからです。SDL2 FPS制限を無効にする方法

具体的には、私は何も描画しませんが、メインループでSDL_GL_SwapWindow()を呼び出すと、各サイクルに約16ミリ秒かかります。この呼び出しがなければ、ほとんど時間がかかりません。 SDL_GL_SwapWindowがメインループ内の唯一の関数呼び出しである場合、16ミリ秒かかることさえあります。これは、SDL2で何らかの種類のfps制限またはvsyncを有効にする必要があることを意味します。だから私の質問は:どのように私は上記の制限を無効にするのですか?

私が見つけた唯一のスレッドは、私の質問に幾分密接に似ていますが、次のとおりです:SDL_GL_SwapWindow bad performance。しかし、このスレッドの答えは本当に私に役立つものではありません。さらに、これはFreeGLUTで行われた同様のコードでは発生しないため、私のコンピュータが原因ではないようです。

すべてのファイルの上に簡単な概要:

jtime.h:  For time measurement 
color.h:  For colored console output (easier to make out errors) 
display.h:  Declaration of class Display 
display.cpp: Implementation of class Display 
main.cpp:  Main function and HandleEvents() 

マイコード:

main.cppに

#include <iostream> 
#include <conio.h> 

#include <display.h> 
#include <glew.h> 
#include <jtime.h> 

void HandleEvents(SDL_Event&& e, jpk::Display& d) 
{ 
    if(e.type == SDL_QUIT) 
     d.GetWindowState() = jpk::Display::WindowState::EXIT; 
} 

int main(int argc, char* argv[]) 
{ 
    jpk::Display display("Test", SDL_WINDOWPOS_UNDEFINED, 
     SDL_WINDOWPOS_UNDEFINED, 400, 400); 

    if(display.GetWindowState() == jpk::Display::WindowState::EXIT) 
     return 1; 
    else 
    { 
     jpk::measure<> ms; 
     while(display.GetWindowState() != jpk::Display::WindowState::EXIT) 
     { 
      ms.start(); 
      display.Update(50, HandleEvents); 
      std::cout << "Time taken: " << ms.stop() << "ms" << std::endl; 
     } 
     return 0; 
    } 
} 

display.cpp

#include <display.h> 
#include <color.h> 
#include <jtime.h> 

#include <glew.h> 
#include <wglew.h> 
#include <iostream> 
#include <vector> 
#include <list> 

jpk::Display::SDL2Helper jpk::Display::helper(SDL_INIT_MODULES, SDL_OUTPUT_MSG); 

jpk::Display::SDL2Helper::SDL2Helper(const uint32_t& f, const bool& o) : 
    init(true) 
{ 
    jpk::measure<> ms; 
    if(SDL_Init(f)) 
    { 
     std::cerr << jpk::color::light_red_f << "[Error] SDL2 could not be " 
       "initialized.\nDetails according to SDL2: " << SDL_GetError() << 
       jpk::color::reset << std::endl; 
     init = false; 
    } 
    else if(o) 
     std::cout << jpk::color::light_green_f << "[Info] SDL2 has been " << 
       "initialized successfully. Time: " << ms.stop() << "ms." << 
       jpk::color::reset << std::endl; 
} 

jpk::Display::SDL2Helper::~SDL2Helper(void) 
{ 
    if(init) 
     SDL_Quit(); 
} 

jpk::Display::Display(const std::string& t, const unsigned int& x, 
         const unsigned int& y, const unsigned int& w, 
         const unsigned int& h, const uint32_t& f, std::string s) : 
         window(nullptr), 
         glContext(nullptr), 
         state(WindowState::READY) 
{ 
    jpk::measure<> ms; 

    window = SDL_CreateWindow(t.c_str(), x, y, w, h, f | SDL_WINDOW_OPENGL | 
      SDL_WINDOW_HIDDEN); 

    if(window == nullptr) 
    { 
     std::cerr << jpk::color::light_red_f << "[Error] The instance of " << 
       "class 'Display' couldn't be created.\nDetails according to " << 
       "SDL2: " << SDL_GetError() << jpk::color::reset << 
       std::endl; 

     state = WindowState::EXIT; 
    } 
    else 
    { 
     glContext = SDL_GL_CreateContext(window); 

     if(glContext == nullptr) 
     { 
      std::cerr << jpk::color::light_red_f << "[Error] The GL Context " << 
        "of the instance of class 'Display' couldn't be " << 
        "initialized.\nDetails according to SDL2: " << 
        SDL_GetError() << jpk::color::reset << std::endl; 
      state = WindowState::EXIT; 
     } 
     else 
     { 
      GLenum error(glewInit()); 

      if(error != GLEW_OK) 
      { 
       std::cerr << jpk::color::light_red_f << "[Error] GLEW " << 
         "failed to initialize.\nDetails according to GLEW: " << 
         glewGetErrorString(error) << jpk::color::reset << 
         std::endl; 
       state = WindowState::EXIT; 
      } 
      else 
      { 
       bool noSupport(false); 
       if(s.length() > 0) 
       { 
        s += " "; 
        size_t found(s.find(" ")); 

        while(found != std::string::npos) 
        { 
         std::string ext(s); 

         ext.erase(found); 
         s.erase(0, found+1); 

         if(!glewIsSupported(ext.c_str()) && 
           !wglewIsSupported(ext.c_str())) 
         { 
          std::cout << jpk::color::light_red_f << "[Error] " << 
            "The following GLEW extension is not " << 
            "supported: " << ext << "." << 
            jpk::color::reset << std::endl; 
          noSupport = true; 
         } 
         found = s.find(" "); 
        } 
       } 

       if(!noSupport) 
       { 
        std::cout << jpk::color::light_green_f << "[Info] The " << 
          "instance of class 'Display' has successfully " << 
          "been created! Time: " << ms.stop() << "ms." << 
          jpk::color::reset << std::endl; 

        if(!(f & SDL_WINDOW_HIDDEN)) 
         SDL_ShowWindow(window); 
       } 
       else 
        state = WindowState::EXIT; 
      } 
     } 
    } 
} 

jpk::Display::~Display(void) 
{ 
    if(glContext != nullptr) 
     SDL_GL_DeleteContext(glContext); 
    if(window != nullptr) 
     SDL_DestroyWindow(window); 
} 

bool jpk::Display::SDL_InitStatus(void) 
{ 
    return helper.init; 
} 

void jpk::Display::Update(const unsigned int& n, void (*f)(SDL_Event&&, jpk::Display&)) 
{ 
    SDL_GL_SwapWindow(window); 
    static std::list<SDL_Event> events; 

    SDL_Event e; 
    while(SDL_PollEvent(&e)) 
     events.push_back(e); 

    if(n != 0) 
     for(unsigned int i(0); i < n ;i++) 
     { 
      f(std::move(events.front()), *this); 
      events.pop_front(); 
     } 
    else 
    { 
     const unsigned int numEvents(events.size()); 
     for(unsigned int i(0); i < numEvents ;i++) 
     { 
      f(std::move(events.front()), *this); 
      events.pop_front(); 
     } 
    } 
} 

void jpk::Display::Show(void) 
{ 
    if(window != nullptr) 
     SDL_ShowWindow(window); 
} 

void jpk::Display::Hide(void) 
{ 
    if(window != nullptr) 
     SDL_HideWindow(window); 
} 

jpk::Display::WindowState& jpk::Display::GetWindowState(void) 
{ 
    return state; 
} 

display.h

#ifndef DISPLAY_H 
#define DISPLAY_H 

#define SDL_MAIN_HANDLED 

#define SDL_INIT_MODULES SDL_INIT_EVERYTHING 
#define SDL_OUTPUT_MSG false 

#include <string> 
#include <iostream> 
#include <SDL.h> 

namespace jpk 
{ 
    class Display 
    { 
    public: 
     enum class WindowState 
     { 
      READY, 
      EXIT 
     }; 

     Display(const std::string& title, const unsigned int& pos_x, 
       const unsigned int& pos_y, const unsigned int& width, 
       const unsigned int& height, const uint32_t& flags = 0, 
       std::string support = ""); 
     ~Display(void); 

     Display(const Display&) = delete; 
     Display& operator=(const Display&) = delete; 

     static bool SDL_InitStatus(void); 
     WindowState& GetWindowState(void); 

     void Update(const unsigned int& numEvents, 
       void (*eventFunc)(SDL_Event&&, jpk::Display&)); 

     void Show(void); 
     void Hide(void); 

    private: 
     struct SDL2Helper 
     { 
      SDL2Helper(const uint32_t& flags, const bool& output = true); 
      ~SDL2Helper(void); 

      SDL2Helper(const SDL2Helper&) = delete; 
      SDL2Helper& operator=(const SDL2Helper&) = delete; 

      bool init; 
     }; 

     SDL_Window* window; 
     SDL_GLContext glContext; 
     WindowState state; 
     static SDL2Helper helper; 
    }; 
} 

#endif /* DISPLAY_H */ 

jtime.h

#ifndef JTIME_H 
#define JTIME_H 

#include <chrono> 
#include <utility> 

#ifndef CLOCK_TYPE 
#define CLOCK_TYPE std::chrono::steady_clock 
#endif // CLOCK_TYPE 

namespace jpk 
{ 
    template<typename TimeT = std::chrono::milliseconds> class measure 
    { 
    public: 
     measure(void) : 
      t(CLOCK_TYPE::now()) 
     {} 
     ~measure(void) {} 

     measure(const measure&) = delete; 
     measure& operator=(const measure&) = delete; 

     void start(void) 
     { 
      t = CLOCK_TYPE::now(); 
     } 

     typename TimeT::rep stop(void) 
     { 
      return std::chrono::duration_cast<TimeT>(CLOCK_TYPE::now()-t).count(); 
     } 

     TimeT stop_chrono(void) 
     { 
      return std::chrono::duration_cast<TimeT>(CLOCK_TYPE::now()-t); 
     } 

     template<typename F, typename... Args> static 
      typename TimeT::rep duration_single(F func, Args&&... args) 
     { 
      auto start(CLOCK_TYPE::now()); 
      std::forward<decltype(func)>(func)(std::forward<Args>(args)...); 
      return std::chrono::duration_cast<TimeT>(CLOCK_TYPE::now()-start).count(); 
     } 

     template<typename F, typename... Args> static typename TimeT::rep 
      duration_average(const unsigned int& tries, F func, Args&&... args) 
     { 
      typename TimeT::rep* times = new typename TimeT::rep[tries]; 
      typename TimeT::rep time(0.0); 

      for(unsigned int i(0); i < tries ;i++) 
       times[i] = duration_single(func, args...); 

      for(unsigned int i(0); i < tries ;i++) 
       time += times[i]; 

      delete[] times; 
      return double(time)/double(tries); 
     } 

     template<typename F, typename... Args> static 
      TimeT duration_chrono(F func, Args&&... args) 
     { 
      auto start(CLOCK_TYPE::now()); 
      std::forward<decltype(func)>(func)(std::forward<Args>(args)...); 
      return std::chrono::duration_cast<TimeT>(CLOCK_TYPE::now()-start); 
     } 

    private: 
     CLOCK_TYPE::time_point t; 
    }; 

    template<typename TimeT = std::chrono::milliseconds> 
     void wait(const typename TimeT::rep& time) 
    { 
     CLOCK_TYPE::time_point start(CLOCK_TYPE::now()); 
     while(std::chrono::duration_cast<TimeT>(CLOCK_TYPE::now() - start).count() 
       < time) 
     { 
      // Just stop doing anything 
     } 
    } 
} 

#endif /* JTIME_H */ 

color.h

#ifndef COLOR_H 
#define COLOR_H 

#include <string> 
#include <iostream> 

#if defined(_WIN32) && !defined(JPK_USE_ANSI) 
#include <windows.h> 
#endif // _WIN32 

namespace jpk 
{ 
    class color_t 
    { 
    public: 
     color_t(const unsigned int& col); 
#if !defined(_WIN32) || defined(JPK_USE_ANSI) 
     color_t(const std::string& esc); 
#endif // _WIN32 
     virtual ~color_t(void); 

     color_t(const color_t&) = delete; 
     color_t& operator=(const color_t&) = delete; 

     void use(std::ostream& out) const; 
     friend std::ostream& operator<<(std::ostream&, const jpk::color_t&); 

    private: 
#if defined(_WIN32) && !defined(JPK_USE_ANSI) 
     const unsigned int c; 

     static bool reset_attr_got; 
     static WORD reset_attr; 
#else 
     const std::string seq; 
#endif // _WIN32 
    }; 

    struct color 
    { 
     enum colors 
     { 
      BLACK_F, 
      BLUE_F, 
      GREEN_F, 
      CYAN_F, 
      RED_F, 
      MAGENTA_F, 
      BROWN_F, 
      GREY_F, 
      DARKGREY_F, 
      LIGHTBLUE_F, 
      LIGHTGREEN_F, 
      LIGHTCYAN_F, 
      LIGHTRED_F, 
      LIGHTMAGENTA_F, 
      YELLOW_F, 
      WHITE_F, 

      BLACK_B, 
      BLUE_B, 
      GREEN_B, 
      CYAN_B, 
      RED_B, 
      MAGENTA_B, 
      YELLOW_B, 
      WHITE_B, 

      RESET 
     }; 

     color(void) = delete; 
     ~color(void) = delete; 

     static color_t black_f; 
     static color_t red_f; 
     static color_t green_f; 
     static color_t brown_f; 
     static color_t blue_f; 
     static color_t magenta_f; 
     static color_t cyan_f; 
     static color_t grey_f; 
     static color_t dark_grey_f; 
     static color_t light_red_f; 
     static color_t light_green_f; 
     static color_t yellow_f; 
     static color_t light_blue_f; 
     static color_t light_magenta_f; 
     static color_t light_cyan_f; 
     static color_t white_f; 

     static color_t black_b; 
     static color_t red_b; 
     static color_t green_b; 
     static color_t yellow_b; 
     static color_t blue_b; 
     static color_t magenta_b; 
     static color_t cyan_b; 
     static color_t white_b; 

     static color_t reset; 
    }; 
} 

#if !defined(_WIN32) || defined(JPK_USE_ANSI) 
    std::string getAnsiEsc(const unsigned int& col) 
    { 
     switch(col) 
     { 
     case jpk::color::BLACK_F:   return "\033[22;30m"; 
     case jpk::color::RED_F:    return "\033[22;31m"; 
     case jpk::color::GREEN_F:   return "\033[22;32m"; 
     case jpk::color::BROWN_F:   return "\033[22;33m"; 
     case jpk::color::BLUE_F:   return "\033[22;34m"; 
     case jpk::color::MAGENTA_F:   return "\033[22;35m"; 
     case jpk::color::CYAN_F:   return "\033[22;36m"; 
     case jpk::color::GREY_F:   return "\033[22;37m"; 
     case jpk::color::DARKGREY_F:  return "\033[01;30m"; 
     case jpk::color::LIGHTRED_F:  return "\033[01;31m"; 
     case jpk::color::LIGHTGREEN_F:  return "\033[01;32m"; 
     case jpk::color::YELLOW_F:   return "\033[01;33m"; 
     case jpk::color::LIGHTBLUE_F:  return "\033[01;34m"; 
     case jpk::color::LIGHTMAGENTA_F: return "\033[01;35m"; 
     case jpk::color::LIGHTCYAN_F:  return "\033[01;36m"; 
     case jpk::color::WHITE_F:   return "\033[01;37m"; 

     case jpk::color::BLACK_B:   return "\033[40m"; 
     case jpk::color::RED_B:    return "\033[41m"; 
     case jpk::color::GREEN_B:   return "\033[42m"; 
     case jpk::color::YELLOW_B:   return "\033[43m"; 
     case jpk::color::BLUE_B:   return "\033[44m"; 
     case jpk::color::MAGENTA_B:   return "\033[45m"; 
     case jpk::color::CYAN_B:   return "\033[46m"; 
     case jpk::color::WHITE_B:   return "\033[47m"; 

     case jpk::color::RESET:    return "\033[0m"; 
     } 
     return ""; 
    } 
#endif // _WIN32 

jpk::color_t::color_t(const unsigned int& col) : 
#if defined(_WIN32) && !defined(JPK_USE_ANSI) 
     c(col) 
    { 
     if(!reset_attr_got) 
     { 
      CONSOLE_SCREEN_BUFFER_INFO csbi; 
      GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); 

      reset_attr = csbi.wAttributes; 
      reset_attr_got = true; 
     } 
#else 
     seq(jpk::getAnsiEsc(col)) 
    {} 

    jpk::color_t::color_t(const std::string& esc) : 
     seq(esc) 
    { 
#endif // _WIN32 
} 

jpk::color_t::~color_t(void) {} 

#if defined(_WIN32) && !defined(JPK_USE_ANSI) 
    bool jpk::color_t::reset_attr_got(false); 
    WORD jpk::color_t::reset_attr(0); 

    void jpk::color_t::use(std::ostream& out) const 
    { 
     if(c <= jpk::color::RESET) 
     { 
      HANDLE hConsole(GetStdHandle(STD_OUTPUT_HANDLE)); 
      CONSOLE_SCREEN_BUFFER_INFO csbi; 
      GetConsoleScreenBufferInfo(hConsole, &csbi); 

      if(c < jpk::color::BLACK_B) 
       SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFFF0) | (WORD)c); 
      else if((c > jpk::color::WHITE_F) && (c < jpk::color::RESET)) 
       SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFF0F) | (((WORD)(c - jpk::color::BLACK_B)) << 4)); 
      else if(c == jpk::color::RESET) 
       SetConsoleTextAttribute(hConsole, reset_attr); 
     } 
    } 

    jpk::color_t jpk::color::black_f(jpk::color::BLACK_F); 
    jpk::color_t jpk::color::red_f(jpk::color::RED_F); 
    jpk::color_t jpk::color::green_f(jpk::color::GREEN_F); 
    jpk::color_t jpk::color::brown_f(jpk::color::BROWN_F); 
    jpk::color_t jpk::color::blue_f(jpk::color::BLUE_F); 
    jpk::color_t jpk::color::magenta_f(jpk::color::MAGENTA_F); 
    jpk::color_t jpk::color::cyan_f(jpk::color::CYAN_F); 
    jpk::color_t jpk::color::grey_f(jpk::color::GREY_F); 
    jpk::color_t jpk::color::dark_grey_f(jpk::color::DARKGREY_F); 
    jpk::color_t jpk::color::light_red_f(jpk::color::LIGHTRED_F); 
    jpk::color_t jpk::color::light_green_f(jpk::color::LIGHTGREEN_F); 
    jpk::color_t jpk::color::yellow_f(jpk::color::YELLOW_F); 
    jpk::color_t jpk::color::light_blue_f(jpk::color::LIGHTBLUE_F); 
    jpk::color_t jpk::color::light_magenta_f(jpk::color::LIGHTMAGENTA_F); 
    jpk::color_t jpk::color::light_cyan_f(jpk::color::LIGHTCYAN_F); 
    jpk::color_t jpk::color::white_f(jpk::color::WHITE_F); 

    jpk::color_t jpk::color::black_b(jpk::color::BLACK_B); 
    jpk::color_t jpk::color::red_b(jpk::color::RED_B); 
    jpk::color_t jpk::color::green_b(jpk::color::GREEN_B); 
    jpk::color_t jpk::color::yellow_b(jpk::color::YELLOW_B); 
    jpk::color_t jpk::color::blue_b(jpk::color::BLUE_B); 
    jpk::color_t jpk::color::magenta_b(jpk::color::MAGENTA_B); 
    jpk::color_t jpk::color::cyan_b(jpk::color::CYAN_B); 
    jpk::color_t jpk::color::white_b(jpk::color::WHITE_B); 

    jpk::color_t jpk::color::reset(jpk::color::RESET); 
#else 
    void jpk::color_t::use(std::ostream& out) const 
    { 
     out << seq; 
    } 

    jpk::color_t jpk::color::black_f("\033[22;30m"); 
    jpk::color_t jpk::color::red_f("\033[22;31m"); 
    jpk::color_t jpk::color::green_f("\033[22;32m"); 
    jpk::color_t jpk::color::brown_f("\033[22;33m"); 
    jpk::color_t jpk::color::blue_f("\033[22;34m"); 
    jpk::color_t jpk::color::magenta_f("\033[22;35m"); 
    jpk::color_t jpk::color::cyan_f("\033[22;36m"); 
    jpk::color_t jpk::color::grey_f("\033[22;37m"); 
    jpk::color_t jpk::color::dark_grey_f("\033[01;30m"); 
    jpk::color_t jpk::color::light_red_f("\033[01;31m"); 
    jpk::color_t jpk::color::light_green_f("\033[01;32m"); 
    jpk::color_t jpk::color::yellow_f("\033[01;33m"); 
    jpk::color_t jpk::color::light_blue_f("\033[01;34m"); 
    jpk::color_t jpk::color::light_magenta_f("\033[01;35m"); 
    jpk::color_t jpk::color::light_cyan_f("\033[01;36m"); 
    jpk::color_t jpk::color::white_f("\033[01;37m"); 

    jpk::color_t jpk::color::black_b("\033[40m"); 
    jpk::color_t jpk::color::red_b("\033[41m"); 
    jpk::color_t jpk::color::green_b("\033[42m"); 
    jpk::color_t jpk::color::yellow_b("\033[43m"); 
    jpk::color_t jpk::color::blue_b("\033[44m"); 
    jpk::color_t jpk::color::magenta_b("\033[45m"); 
    jpk::color_t jpk::color::cyan_b("\033[46m"); 
    jpk::color_t jpk::color::white_b("\033[47m"); 

    jpk::color_t jpk::color::reset("\033[0m"); 
#endif // _WIN32 

namespace jpk 
{ 
    std::ostream& operator<<(std::ostream& out, const color_t& col) 
    { 
     col.use(out); 
     return out; 
    } 
} 

#endif /* COLOR_H */ 

答えて

1

これはSDLではなく、あなたのグラフィックスドライバのOpenGLの設定とは何の関係もありません。バッファを交換するとき、操作は表示のリフレッシュに同期されるので、引き裂きアーチファクトは現れません。プログラムが表示されているフレームよりも速く描画されている場合、表示されている1つのフレーム内で複数の「ストライプ」のレンダリングが表示され、それぞれレンダリングにかかる​​時間だけ遅れて表示されます。 一般に、実際には、再描画をFPSに制限し、ディスプレイの更新に同期させる必要があります。プログラム内で選択的に有効または無効にするAPIはいくつかあります(ただし、ドライバは常に無効にすることができます)。 OpenGL(API SDL_GL_SwapBuffers)の場合、これを制御するAPIを経由して、いわゆる「スワップインターバル」APIが使用されます。詳細はOpenGL wikiで確認できます。https://www.opengl.org/wiki/Swap_Interval

+0

これは、SDL2を使用しているときに発生しますが、FreeGLUTを使用して同じことを行うときはどうしてですか? FreeGLUTはそれを無効にしますか? – JPKing64

+0

@ JPKing64:SDL2は実際にスワップインターバルAPI(https://wiki.libsdl.org/SDL_GL_SetSwapIntervalを参照)を「知っている」だけで、特定の初期値に設定することができますが、FreeGLUTにはスワップインターバルAPIコンテキストを作成した後に「そのまま」残します。 – datenwolf

+0

助けてくれてありがとう。さらなる調査の後、私はそれがどのように機能するかを知った。まだ時間がある場合、SDL2は新しい画像を表示するまで何もしません。毎回3msを待っても、ほとんどの場合、fpsは60に維持されます。 正直言って、私はSDL2に今はそれをさせるだけです。しかし、将来、ある時点では、fpsの制限を無効にすると便利かもしれないと思ったので、Displayクラスのスワップ間隔を無効にするオプションを実装します。 – JPKing64

関連する問題