diff --git a/include/settings.hpp b/include/settings.hpp index 1685c3bc..d94a7f37 100644 --- a/include/settings.hpp +++ b/include/settings.hpp @@ -67,7 +67,8 @@ extern bool tooltips; extern bool cpuMeter; extern bool lockModules; extern bool squeezeModules; -extern int frameSwapInterval; +/** Maximum screen redraw frequency in Hz, or 0 for unlimited. */ +extern float frameRateLimit; extern float autosaveInterval; extern bool skipLoadOnLaunch; extern std::list recentPatchPaths; diff --git a/src/app/MenuBar.cpp b/src/app/MenuBar.cpp index 44117e64..eb8dd108 100644 --- a/src/app/MenuBar.cpp +++ b/src/app/MenuBar.cpp @@ -409,13 +409,12 @@ struct ViewButton : MenuButton { APP->window->setFullScreen(!fullscreen); })); - double frameRate = APP->window->getMonitorRefreshRate() / settings::frameSwapInterval; - menu->addChild(createSubmenuItem("Frame rate", string::f("%.0f Hz", frameRate), [=](ui::Menu* menu) { + menu->addChild(createSubmenuItem("Frame rate", string::f("%.0f Hz", settings::frameRateLimit), [=](ui::Menu* menu) { for (int i = 1; i <= 6; i++) { double frameRate = APP->window->getMonitorRefreshRate() / i; menu->addChild(createCheckMenuItem(string::f("%.0f Hz", frameRate), "", - [=]() {return settings::frameSwapInterval == i;}, - [=]() {settings::frameSwapInterval = i;} + [=]() {return settings::frameRateLimit == frameRate;}, + [=]() {settings::frameRateLimit = frameRate;} )); } })); diff --git a/src/settings.cpp b/src/settings.cpp index 4511a4b8..621a395a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -42,10 +42,10 @@ bool cpuMeter = false; bool lockModules = false; bool squeezeModules = true; #if defined ARCH_MAC - // Most Mac GPUs can't handle rendering the screen every frame, so use ~30 Hz by default. - int frameSwapInterval = 2; + // Most Mac GPUs can't handle rendering the screen every frame, so use 30 Hz by default. + float frameRateLimit = 30.f; #else - int frameSwapInterval = 1; + float frameRateLimit = 60.f; #endif float autosaveInterval = 15.0; bool skipLoadOnLaunch = false; @@ -158,7 +158,7 @@ json_t* toJson() { json_object_set_new(rootJ, "squeezeModules", json_boolean(squeezeModules)); - json_object_set_new(rootJ, "frameSwapInterval", json_integer(frameSwapInterval)); + json_object_set_new(rootJ, "frameRateLimit", json_real(frameRateLimit)); json_object_set_new(rootJ, "autosaveInterval", json_real(autosaveInterval)); @@ -347,9 +347,20 @@ void fromJson(json_t* rootJ) { if (squeezeModulesJ) squeezeModules = json_boolean_value(squeezeModulesJ); + // Legacy setting in Rack <2.2 json_t* frameSwapIntervalJ = json_object_get(rootJ, "frameSwapInterval"); - if (frameSwapIntervalJ) - frameSwapInterval = json_integer_value(frameSwapIntervalJ); + if (frameSwapIntervalJ) { + // Assume 60 Hz monitor refresh rate. + int frameSwapInterval = json_integer_value(frameSwapIntervalJ); + if (frameSwapInterval > 0) + frameRateLimit = 60.f / frameSwapInterval; + else + frameRateLimit = 0.f; + } + + json_t* frameRateLimitJ = json_object_get(rootJ, "frameRateLimit"); + if (frameRateLimitJ) + frameRateLimit = json_number_value(frameRateLimitJ); json_t* autosaveIntervalJ = json_object_get(rootJ, "autosaveInterval"); if (autosaveIntervalJ) diff --git a/src/window/Window.cpp b/src/window/Window.cpp index 30d0162e..986f5e7f 100644 --- a/src/window/Window.cpp +++ b/src/window/Window.cpp @@ -89,8 +89,8 @@ struct Window::Internal { int frame = 0; bool ignoreNextMouseDelta = false; - int frameSwapInterval = -1; double monitorRefreshRate = 0.0; + int frameSwapInterval = -1; double frameTime = 0.0; double lastFrameDuration = 0.0; @@ -298,7 +298,6 @@ Window::Window() { glfwSetInputMode(win, GLFW_LOCK_KEY_MODS, 1); glfwMakeContextCurrent(win); - glfwSwapInterval(1); const GLFWvidmode* monitorMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); if (monitorMode->refreshRate > 0) { internal->monitorRefreshRate = monitorMode->refreshRate; @@ -433,9 +432,16 @@ void Window::step() { // In case glfwPollEvents() sets another OpenGL context glfwMakeContextCurrent(win); - if (settings::frameSwapInterval != internal->frameSwapInterval) { - glfwSwapInterval(settings::frameSwapInterval); - internal->frameSwapInterval = settings::frameSwapInterval; + + // Set swap interval + int frameSwapInterval = 0; + if (settings::frameRateLimit > 0.f) { + frameSwapInterval = std::ceil(internal->monitorRefreshRate / settings::frameRateLimit); + } + + if (frameSwapInterval != internal->frameSwapInterval) { + glfwSwapInterval(frameSwapInterval); + internal->frameSwapInterval = frameSwapInterval; } // Call cursorPosCallback every frame, not just when the mouse moves @@ -711,7 +717,7 @@ double Window::getLastFrameDuration() { double Window::getFrameDurationRemaining() { - double frameDurationDesired = internal->frameSwapInterval / internal->monitorRefreshRate; + double frameDurationDesired = 1.f / settings::frameRateLimit; return frameDurationDesired - (system::getTime() - internal->frameTime); }