From c725894abd552ba07a9ebe0865b96094dcd05592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Sun, 12 Feb 2023 13:58:54 +0100 Subject: [PATCH] Emscripten: Add "vsync" FPS limit mode Would be really useful for native versions too, but it's more complicated to pull off. For now, vsync on native is the same as tpt.setfpscap(2). --- src/Config.template.h | 1 + src/FpsLimit.h | 14 ++++++++++++++ src/PowderToySDL.cpp | 7 ++++--- src/PowderToySDL.h | 3 +++ src/PowderToySDLCommon.cpp | 4 ++++ src/PowderToySDLEmscripten.cpp | 32 +++++++++++++++----------------- src/gui/interface/Engine.cpp | 24 +++++++++++++++++++++--- src/gui/interface/Engine.h | 10 +++++++++- src/lua/LegacyLuaAPI.cpp | 28 ++++++++++++++++++++++++++-- src/meson.build | 3 +++ 10 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/FpsLimit.h diff --git a/src/Config.template.h b/src/Config.template.h index 31970599c..684e36061 100644 --- a/src/Config.template.h +++ b/src/Config.template.h @@ -17,6 +17,7 @@ constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@; constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@; constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@; constexpr bool FFTW_PLAN_MEASURE = @FFTW_PLAN_MEASURE@; +constexpr bool DEFAULT_VSYNC = @DEFAULT_VSYNC@; constexpr char PATH_SEP_CHAR = '@PATH_SEP_CHAR@'; constexpr char SERVER[] = "@SERVER@"; diff --git a/src/FpsLimit.h b/src/FpsLimit.h new file mode 100644 index 000000000..5d8a69335 --- /dev/null +++ b/src/FpsLimit.h @@ -0,0 +1,14 @@ +#pragma once +#include + +struct FpsLimitVsync +{ +}; +struct FpsLimitNone +{ +}; +struct FpsLimitExplicit +{ + float value; +}; +using FpsLimit = std::variant; diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp index 378d86da6..55f735f01 100644 --- a/src/PowderToySDL.cpp +++ b/src/PowderToySDL.cpp @@ -390,16 +390,17 @@ void EngineProcess() blit(engine.g->Data()); } - auto fpsLimit = ui::Engine::Ref().FpsLimit; + auto fpsLimit = ui::Engine::Ref().GetFpsLimit(); auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000); oldFrameStart = frameStart; frameStart = now; - if (fpsLimit > 2) + if (auto *fpsLimitExplicit = std::get_if(&fpsLimit)) { - auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimit); + auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimitExplicit->value); auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration; auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U; frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration); SDL_Delay((frameStart - now) / UINT64_C(1'000'000)); } + // TODO: else if (auto *fpsLimitExplicit = std::get_if(&fpsLimit)) } diff --git a/src/PowderToySDL.h b/src/PowderToySDL.h index 012a5f3e0..728daf4e7 100644 --- a/src/PowderToySDL.h +++ b/src/PowderToySDL.h @@ -1,8 +1,10 @@ #pragma once #include "common/String.h" #include "graphics/Pixel.h" +#include "FpsLimit.h" #include #include +#include extern int desktopWidth; extern int desktopHeight; @@ -40,6 +42,7 @@ void blit(pixel *vid); void SDLOpen(); void SDLClose(); void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_); +void SetFpsLimit(FpsLimit newFpsLimit); bool RecreateWindow(); void LoadWindowPosition(); void SaveWindowPosition(); diff --git a/src/PowderToySDLCommon.cpp b/src/PowderToySDLCommon.cpp index 80ff83fed..294c45a9d 100644 --- a/src/PowderToySDLCommon.cpp +++ b/src/PowderToySDLCommon.cpp @@ -8,3 +8,7 @@ void MainLoop() EngineProcess(); } } + +void SetFpsLimit(FpsLimit newFpsLimit) +{ +} diff --git a/src/PowderToySDLEmscripten.cpp b/src/PowderToySDLEmscripten.cpp index d0705cb4b..d76dbc2c9 100644 --- a/src/PowderToySDLEmscripten.cpp +++ b/src/PowderToySDLEmscripten.cpp @@ -3,35 +3,33 @@ #include #include -static float lastFpsLimit; -static void updateFpsLimit() +void SetFpsLimit(FpsLimit newFpsLimit) { - lastFpsLimit = ui::Engine::Ref().FpsLimit; - if (lastFpsLimit == 60.0f) // TODO: rework FPS cap so 60.0 is not the default + static bool mainLoopSet = false; + if (!mainLoopSet) + { + emscripten_set_main_loop(EngineProcess, 0, 0); + mainLoopSet = true; + } + if (auto *fpsLimitVsync = std::get_if(&newFpsLimit)) { emscripten_set_main_loop_timing(EM_TIMING_RAF, 1); std::cerr << "implicit fps limit via vsync" << std::endl; } else { - auto delay = int(1000.f / lastFpsLimit); + auto delay = 0; + if (auto *fpsLimitExplicit = std::get_if(&newFpsLimit)) + { + delay = int(1000.f / fpsLimitExplicit->value); + } emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, delay); std::cerr << "explicit fps limit: " << delay << "ms delays" << std::endl; } } -static void mainLoopBody() -{ - EngineProcess(); - if (lastFpsLimit != ui::Engine::Ref().FpsLimit) - { - updateFpsLimit(); - } -} - void MainLoop() { - emscripten_set_main_loop(mainLoopBody, 0, 0); - updateFpsLimit(); - mainLoopBody(); + SetFpsLimit(ui::Engine::Ref().GetFpsLimit()); + EngineProcess(); } diff --git a/src/gui/interface/Engine.cpp b/src/gui/interface/Engine.cpp index 8a6768acf..7433a65e8 100644 --- a/src/gui/interface/Engine.cpp +++ b/src/gui/interface/Engine.cpp @@ -1,4 +1,5 @@ #include "Engine.h" +#include "Config.h" #include "PowderToySDL.h" #include "Window.h" #include "common/platform/Platform.h" @@ -10,7 +11,6 @@ using namespace ui; Engine::Engine(): - FpsLimit(60.0f), drawingFrequencyLimit(0), Scale(1), Fullscreen(false), @@ -27,7 +27,15 @@ Engine::Engine(): mousexp_(0), mouseyp_(0) { - SetFps(FpsLimit); // populate dt with whatever that makes any sort of sense + SetFpsLimit(fpsLimit); // populate dt with whatever that makes any sort of sense + if constexpr (DEFAULT_VSYNC) + { + SetFpsLimit(FpsLimitVsync{}); + } + else + { + SetFpsLimit(FpsLimitExplicit{ 60.0f }); + } } Engine::~Engine() @@ -41,6 +49,12 @@ Engine::~Engine() } } +void Engine::SetFpsLimit(FpsLimit newFpsLimit) +{ + fpsLimit = newFpsLimit; + ::SetFpsLimit(fpsLimit); +} + void Engine::Begin() { //engine is now ready @@ -194,10 +208,14 @@ void Engine::Draw() void Engine::SetFps(float fps) { this->fps = fps; - if(FpsLimit > 2.0f) + if (std::holds_alternative(fpsLimit)) + { this->dt = 60/fps; + } else + { this->dt = 1.0f; + } } void Engine::onKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) diff --git a/src/gui/interface/Engine.h b/src/gui/interface/Engine.h index 83d7d3adb..4a39ce781 100644 --- a/src/gui/interface/Engine.h +++ b/src/gui/interface/Engine.h @@ -6,6 +6,7 @@ #include "graphics/Pixel.h" #include "gui/interface/Point.h" #include +#include "FpsLimit.h" class Graphics; namespace ui @@ -76,7 +77,13 @@ namespace ui //void SetState(Window* state); //inline State* GetState() { return state_; } inline Window* GetWindow() { return state_; } - float FpsLimit; + + void SetFpsLimit(FpsLimit newFpsLimit); + FpsLimit GetFpsLimit() const + { + return fpsLimit; + } + int drawingFrequencyLimit; Graphics * g; int Scale; @@ -85,6 +92,7 @@ namespace ui unsigned int FrameIndex; private: + FpsLimit fpsLimit; bool altFullscreen; bool forceIntegerScaling = true; bool resizable; diff --git a/src/lua/LegacyLuaAPI.cpp b/src/lua/LegacyLuaAPI.cpp index 3d4bdecf8..93049d1eb 100644 --- a/src/lua/LegacyLuaAPI.cpp +++ b/src/lua/LegacyLuaAPI.cpp @@ -1222,13 +1222,37 @@ int luatpt_setfpscap(lua_State* l) int acount = lua_gettop(l); if (acount == 0) { - lua_pushnumber(l, ui::Engine::Ref().FpsLimit); + auto fpsLimit = ui::Engine::Ref().GetFpsLimit(); + if (std::holds_alternative(fpsLimit)) + { + lua_pushliteral(l, "vsync"); + } + else if (std::holds_alternative(fpsLimit)) + { + lua_pushnumber(l, 2); + } + else + { + lua_pushnumber(l, std::get(fpsLimit).value); + } return 1; } + if (lua_isstring(l, 1) && byteStringEqualsLiteral(tpt_lua_toByteString(l, 1), "vsync")) + { + ui::Engine::Ref().SetFpsLimit(FpsLimitVsync{}); + return 0; + } float fpscap = luaL_checknumber(l, 1); if (fpscap < 2) + { return luaL_error(l, "fps cap too small"); - ui::Engine::Ref().FpsLimit = fpscap; + } + if (fpscap == 2) + { + ui::Engine::Ref().SetFpsLimit(FpsLimitNone{}); + return 0; + } + ui::Engine::Ref().SetFpsLimit(FpsLimitExplicit{ fpscap }); return 0; } diff --git a/src/meson.build b/src/meson.build index 96f97a708..14e56386f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -50,10 +50,12 @@ if host_platform == 'emscripten' powder_files += files( 'PowderToySDLEmscripten.cpp', ) + conf_data.set('DEFAULT_VSYNC', 'true') else powder_files += files( 'PowderToySDLCommon.cpp', ) + conf_data.set('DEFAULT_VSYNC', 'false') endif if is_x86 powder_files += files('X86KillDenormals.cpp') @@ -66,6 +68,7 @@ render_files = files( font_files = files( 'PowderToyFontEditor.cpp', 'PowderToySDL.cpp', + 'PowderToySDLCommon.cpp', ) common_files = files(