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).
This commit is contained in:
Tamás Bálint Misius 2023-02-12 13:58:54 +01:00
parent 9f71eb9d77
commit c725894abd
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
10 changed files with 100 additions and 26 deletions

View File

@ -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@";

14
src/FpsLimit.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <variant>
struct FpsLimitVsync
{
};
struct FpsLimitNone
{
};
struct FpsLimitExplicit
{
float value;
};
using FpsLimit = std::variant<FpsLimitVsync, FpsLimitNone, FpsLimitExplicit>;

View File

@ -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<FpsLimitExplicit>(&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<FpsLimitVsync>(&fpsLimit))
}

View File

@ -1,8 +1,10 @@
#pragma once
#include "common/String.h"
#include "graphics/Pixel.h"
#include "FpsLimit.h"
#include <cstdint>
#include <SDL.h>
#include <variant>
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();

View File

@ -8,3 +8,7 @@ void MainLoop()
EngineProcess();
}
}
void SetFpsLimit(FpsLimit newFpsLimit)
{
}

View File

@ -3,35 +3,33 @@
#include <emscripten.h>
#include <iostream>
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<FpsLimitVsync>(&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<FpsLimitExplicit>(&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();
}

View File

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

View File

@ -6,6 +6,7 @@
#include "graphics/Pixel.h"
#include "gui/interface/Point.h"
#include <climits>
#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;

View File

@ -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<FpsLimitVsync>(fpsLimit))
{
lua_pushliteral(l, "vsync");
}
else if (std::holds_alternative<FpsLimitNone>(fpsLimit))
{
lua_pushnumber(l, 2);
}
else
{
lua_pushnumber(l, std::get<FpsLimitExplicit>(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;
}

View File

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