如何禁用SDL2 FPS限制

时间:2016-10-02 15:25:28

标签: opengl sdl-2 glew vsync

我正在使用SDL 2.0.4和GLEW 1.13.0,似乎SDL2中存在某种fps限制。我知道这一点,因为我做了一些时间测量。

更具体地说,我没有绘制任何东西,但如果我在主循环中调用SDL_GL_SwapWindow(),则每个循环大约需要16毫秒。没有这个电话,几乎没有时间。当SDL_GL_SwapWindow是主循环中唯一的函数调用时,它甚至需要16毫秒。这意味着,在SDL2中必须启用某种fps限制或vsync。所以我的问题是:如何禁用上述限制?

我找到的唯一一个与我的问题非常相似的主题是:SDL_GL_SwapWindow bad performance。但是这个帖子中的答案并没有对我有所帮助。此外,这似乎不是由我的计算机引起的,因为使用FreeGLUT完成的类似代码不会出现此问题。

所有文件的简要概述:

INotifyCollectionChanged

我的代码:

的main.cpp

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()

display.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.h

#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;
}

jtime.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 */

color.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 */

1 个答案:

答案 0 :(得分:1)

这与SDL无关,但与图形驱动程序的OpenGL设置无关。当交换缓冲区时,操作可以与显示器刷新同步,从而不会出现撕裂假象。如果您的程序绘制速度快于显示器能够重绘,那么在一个显示的框架内可以显示几个&#34;条纹&#34;渲染将出现,每个延迟的时间是渲染第一个。 因此,通常你实际上_want_你的重绘是FPS限制并同步到显示刷新。有许多API可以在程序中有选择地启用或禁用它(尽管驱动程序总是可以选择覆盖那个)。在OpenGL(它是API SDL_GL_SwapBuffers)的情况下,通过API来控制这是所谓的&#34;交换间隔&#34; API。详细信息可以在OpenGL wiki中找到:https://www.opengl.org/wiki/Swap_Interval