auto framerate mode
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run

This commit is contained in:
Isaac0-dev 2025-07-26 09:56:14 +10:00
parent 0a84ca725e
commit 46b1a8bbb9
15 changed files with 73 additions and 19 deletions

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Inverze pravé páčky osy X"
INVERT_RIGHT_Y = "Inverze pravé páčky osy Y" INVERT_RIGHT_Y = "Inverze pravé páčky osy Y"
[DISPLAY] [DISPLAY]
MANUAL = "Manuální"
AUTO = "Auto"
DISPLAY = "VIDEO" DISPLAY = "VIDEO"
FULLSCREEN = "Celá obrazovka" FULLSCREEN = "Celá obrazovka"
FORCE_4BY3 = "Vždy 4:3" FORCE_4BY3 = "Vždy 4:3"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Rechter joystick X-as omkeren"
INVERT_RIGHT_Y = "Rechter joystick Y-as omkeren" INVERT_RIGHT_Y = "Rechter joystick Y-as omkeren"
[DISPLAY] [DISPLAY]
MANUAL = "Handmatig"
AUTO = "Auto"
DISPLAY = "WEERGAVE" DISPLAY = "WEERGAVE"
FULLSCREEN = "Volledig scherm" FULLSCREEN = "Volledig scherm"
FORCE_4BY3 = "Forceer 4:3" FORCE_4BY3 = "Forceer 4:3"

View file

@ -147,7 +147,9 @@ FULLSCREEN = "Fullscreen"
FORCE_4BY3 = "Force 4:3" FORCE_4BY3 = "Force 4:3"
PRELOAD_TEXTURES = "Preload Textures" PRELOAD_TEXTURES = "Preload Textures"
VSYNC = "VSync" VSYNC = "VSync"
UNCAPPED_FRAMERATE = "Uncapped Framerate" AUTO = "Auto"
MANUAL = "Manual"
UNCAPPED_FRAMERATE = "Uncapped"
FRAME_LIMIT = "Frame Limit" FRAME_LIMIT = "Frame Limit"
FAST = "Fast" FAST = "Fast"
ACCURATE = "Accurate" ACCURATE = "Accurate"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Inverser l'axe X du stick droit"
INVERT_RIGHT_Y = "Inverser l'axe Y du stick droit" INVERT_RIGHT_Y = "Inverser l'axe Y du stick droit"
[DISPLAY] [DISPLAY]
MANUAL = "Manuel"
AUTO = "Voiture"
DISPLAY = "AFFICHAGE" DISPLAY = "AFFICHAGE"
FULLSCREEN = "Plein Écran" FULLSCREEN = "Plein Écran"
FORCE_4BY3 = "Forcer l'affichage 4:3" FORCE_4BY3 = "Forcer l'affichage 4:3"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Rechter Stick X-Achse invertieren"
INVERT_RIGHT_Y = "Rechter Stick Y-Achse invertieren" INVERT_RIGHT_Y = "Rechter Stick Y-Achse invertieren"
[DISPLAY] [DISPLAY]
MANUAL = "Handbuch"
AUTO = "Auto"
DISPLAY = "ANZEIGE" DISPLAY = "ANZEIGE"
FULLSCREEN = "Vollbildmodus" FULLSCREEN = "Vollbildmodus"
FORCE_4BY3 = "Erzwinge 4:3" FORCE_4BY3 = "Erzwinge 4:3"

View file

@ -140,6 +140,8 @@ INVERT_RIGHT_X = "Invertire l'asse X dello stick destro"
INVERT_RIGHT_Y = "Invertire l'asse Y dello stick destro" INVERT_RIGHT_Y = "Invertire l'asse Y dello stick destro"
[DISPLAY] [DISPLAY]
MANUAL = "Manuale"
AUTO = "Auto"
DISPLAY = "GRAFICA" DISPLAY = "GRAFICA"
FULLSCREEN = "Schermo intero" FULLSCREEN = "Schermo intero"
FORCE_4BY3 = "Force 4:3" FORCE_4BY3 = "Force 4:3"

View file

@ -143,6 +143,8 @@ INVERT_RIGHT_X = "右スティックX軸の反転"
INVERT_RIGHT_Y = "右スティックY軸の反転" INVERT_RIGHT_Y = "右スティックY軸の反転"
[DISPLAY] [DISPLAY]
MANUAL = "マニュアル"
AUTO = "車"
DISPLAY = "DISPLAY" DISPLAY = "DISPLAY"
FULLSCREEN = "フルスクリーン" FULLSCREEN = "フルスクリーン"
FORCE_4BY3 = "画面比を4:3に強制" FORCE_4BY3 = "画面比を4:3に強制"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Odwrócenie osi X prawego drążka"
INVERT_RIGHT_Y = "Odwrócenie osi Y prawego drążka" INVERT_RIGHT_Y = "Odwrócenie osi Y prawego drążka"
[DISPLAY] [DISPLAY]
MANUAL = "Podręcznik"
AUTO = "Samochód"
DISPLAY = "WYŚWIETLANIE" DISPLAY = "WYŚWIETLANIE"
FULLSCREEN = "Pełny Ekran" FULLSCREEN = "Pełny Ekran"
FORCE_4BY3 = "Wymuś proporcję 4:3" FORCE_4BY3 = "Wymuś proporcję 4:3"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Inverter eixo X do analógico direito"
INVERT_RIGHT_Y = "Inverter eixo Y do analógico direito" INVERT_RIGHT_Y = "Inverter eixo Y do analógico direito"
[DISPLAY] [DISPLAY]
MANUAL = "Manual"
AUTO = "Automóvel"
DISPLAY = "VÍDEO" DISPLAY = "VÍDEO"
FULLSCREEN = "Tela cheia" FULLSCREEN = "Tela cheia"
FORCE_4BY3 = "Forçar 4:3" FORCE_4BY3 = "Forçar 4:3"

View file

@ -141,6 +141,8 @@ INVERT_RIGHT_X = "Инвертировать правый стик оси X"
INVERT_RIGHT_Y = "Инвертировать правый стик по оси Y" INVERT_RIGHT_Y = "Инвертировать правый стик по оси Y"
[DISPLAY] [DISPLAY]
MANUAL = "Руководство"
AUTO = "Автомобиль"
DISPLAY = "DISPLAY" DISPLAY = "DISPLAY"
FULLSCREEN = "Полноэкранный режим" FULLSCREEN = "Полноэкранный режим"
FORCE_4BY3 = "Экран 4:3" FORCE_4BY3 = "Экран 4:3"

View file

@ -142,6 +142,8 @@ INVERT_RIGHT_X = "Invertir Stick derecho eje X"
INVERT_RIGHT_Y = "Invertir el eje Y del stick derecho" INVERT_RIGHT_Y = "Invertir el eje Y del stick derecho"
[DISPLAY] [DISPLAY]
MANUAL = "Manual"
AUTO = "Coche"
DISPLAY = "PANTALLA" DISPLAY = "PANTALLA"
FULLSCREEN = "Pantalla completa" FULLSCREEN = "Pantalla completa"
FORCE_4BY3 = "Forzar 4:3" FORCE_4BY3 = "Forzar 4:3"

View file

@ -84,7 +84,7 @@ ConfigStick configStick = { 0 };
// display settings // display settings
unsigned int configFiltering = 2; // 0 = Nearest, 1 = Bilinear, 2 = Trilinear unsigned int configFiltering = 2; // 0 = Nearest, 1 = Bilinear, 2 = Trilinear
bool configShowFPS = false; bool configShowFPS = false;
bool configUncappedFramerate = true; enum RefreshRateMode configFramerateMode = RRM_AUTO;
unsigned int configFrameLimit = 60; unsigned int configFrameLimit = 60;
unsigned int configInterpolationMode = 1; unsigned int configInterpolationMode = 1;
unsigned int configDrawDistance = 4; unsigned int configDrawDistance = 4;
@ -226,7 +226,7 @@ static const struct ConfigOption options[] = {
// display settings // display settings
{.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering}, {.name = "texture_filtering", .type = CONFIG_TYPE_UINT, .uintValue = &configFiltering},
{.name = "show_fps", .type = CONFIG_TYPE_BOOL, .boolValue = &configShowFPS}, {.name = "show_fps", .type = CONFIG_TYPE_BOOL, .boolValue = &configShowFPS},
{.name = "uncapped_framerate", .type = CONFIG_TYPE_BOOL, .boolValue = &configUncappedFramerate}, {.name = "framerate_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configFramerateMode},
{.name = "frame_limit", .type = CONFIG_TYPE_UINT, .uintValue = &configFrameLimit}, {.name = "frame_limit", .type = CONFIG_TYPE_UINT, .uintValue = &configFrameLimit},
{.name = "interpolation_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configInterpolationMode}, {.name = "interpolation_mode", .type = CONFIG_TYPE_UINT, .uintValue = &configInterpolationMode},
{.name = "coop_draw_distance", .type = CONFIG_TYPE_UINT, .uintValue = &configDrawDistance}, {.name = "coop_draw_distance", .type = CONFIG_TYPE_UINT, .uintValue = &configDrawDistance},
@ -776,6 +776,7 @@ NEXT_OPTION:
fs_close(file); fs_close(file);
if (configFramerateMode < 0 || configFramerateMode > RRM_MAX) { configFramerateMode = 0; }
if (configFrameLimit < 30) { configFrameLimit = 30; } if (configFrameLimit < 30) { configFrameLimit = 30; }
if (configFrameLimit > 3000) { configFrameLimit = 3000; } if (configFrameLimit > 3000) { configFrameLimit = 3000; }

View file

@ -36,6 +36,13 @@ typedef struct {
bool invertRightY; bool invertRightY;
} ConfigStick; } ConfigStick;
enum RefreshRateMode {
RRM_AUTO,
RRM_MANUAL,
RRM_UNLIMITED,
RRM_MAX
};
extern char configSaveNames[4][MAX_SAVE_NAME_STRING]; extern char configSaveNames[4][MAX_SAVE_NAME_STRING];
// display settings // display settings
@ -43,7 +50,7 @@ extern ConfigWindow configWindow;
extern ConfigStick configStick; extern ConfigStick configStick;
extern unsigned int configFiltering; extern unsigned int configFiltering;
extern bool configShowFPS; extern bool configShowFPS;
extern bool configUncappedFramerate; extern enum RefreshRateMode configFramerateMode;
extern unsigned int configFrameLimit; extern unsigned int configFrameLimit;
extern unsigned int configInterpolationMode; extern unsigned int configInterpolationMode;
extern unsigned int configDrawDistance; extern unsigned int configDrawDistance;

View file

@ -18,9 +18,9 @@ static void djui_panel_display_apply(UNUSED struct DjuiBase* caller) {
configWindow.settings_changed = true; configWindow.settings_changed = true;
} }
static void djui_panel_display_uncapped_change(UNUSED struct DjuiBase* caller) { static void djui_panel_display_framerate_mode_change(UNUSED struct DjuiBase* caller) {
djui_base_set_enabled(&sFrameLimitInput->base, !configUncappedFramerate); djui_base_set_enabled(&sFrameLimitInput->base, configFramerateMode == RRM_MANUAL);
djui_base_set_enabled(&sInterpolationSelectionBox->base, (configFrameLimit > 30 || (configFrameLimit <= 30 && configUncappedFramerate))); djui_base_set_enabled(&sInterpolationSelectionBox->base, (configFrameLimit > 30 || configFramerateMode != RRM_MANUAL));
} }
static void djui_panel_display_frame_limit_text_change(struct DjuiBase* caller) { static void djui_panel_display_frame_limit_text_change(struct DjuiBase* caller) {
@ -32,7 +32,7 @@ static void djui_panel_display_frame_limit_text_change(struct DjuiBase* caller)
} else { } else {
djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255); djui_inputbox_set_text_color(inputbox1, 255, 0, 0, 255);
} }
djui_base_set_enabled(&sInterpolationSelectionBox->base, (configFrameLimit > 30 || (configFrameLimit <= 30 && configUncappedFramerate))); djui_base_set_enabled(&sInterpolationSelectionBox->base, (configFrameLimit > 30 || configFramerateMode != RRM_MANUAL));
} }
static void djui_panel_display_msaa_change(UNUSED struct DjuiBase* caller) { static void djui_panel_display_msaa_change(UNUSED struct DjuiBase* caller) {
@ -64,7 +64,9 @@ void djui_panel_display_create(struct DjuiBase* caller) {
djui_checkbox_create(body, DLANG(DISPLAY, FORCE_4BY3), &configForce4By3, djui_panel_display_apply); djui_checkbox_create(body, DLANG(DISPLAY, FORCE_4BY3), &configForce4By3, djui_panel_display_apply);
djui_checkbox_create(body, DLANG(DISPLAY, SHOW_FPS), &configShowFPS, NULL); djui_checkbox_create(body, DLANG(DISPLAY, SHOW_FPS), &configShowFPS, NULL);
djui_checkbox_create(body, DLANG(DISPLAY, VSYNC), &configWindow.vsync, djui_panel_display_apply); djui_checkbox_create(body, DLANG(DISPLAY, VSYNC), &configWindow.vsync, djui_panel_display_apply);
djui_checkbox_create(body, DLANG(DISPLAY, UNCAPPED_FRAMERATE), &configUncappedFramerate, djui_panel_display_uncapped_change);
char* framerateModeChoices[3] = { DLANG(DISPLAY, AUTO), DLANG(DISPLAY, MANUAL), DLANG(DISPLAY, UNCAPPED_FRAMERATE) };
djui_selectionbox_create(body, DLANG(DISPLAY, Framerate Mode), framerateModeChoices, 3, &configFramerateMode, djui_panel_display_framerate_mode_change);
struct DjuiRect* rect1 = djui_rect_container_create(body, 32); struct DjuiRect* rect1 = djui_rect_container_create(body, 32);
{ {
@ -85,13 +87,13 @@ void djui_panel_display_create(struct DjuiBase* caller) {
snprintf(frameLimitString, 32, "%d", configFrameLimit); snprintf(frameLimitString, 32, "%d", configFrameLimit);
djui_inputbox_set_text(inputbox1, frameLimitString); djui_inputbox_set_text(inputbox1, frameLimitString);
djui_interactable_hook_value_change(&inputbox1->base, djui_panel_display_frame_limit_text_change); djui_interactable_hook_value_change(&inputbox1->base, djui_panel_display_frame_limit_text_change);
djui_base_set_enabled(&inputbox1->base, !configUncappedFramerate); djui_base_set_enabled(&inputbox1->base, configFramerateMode == RRM_MANUAL);
sFrameLimitInput = inputbox1; sFrameLimitInput = inputbox1;
} }
char* interpChoices[2] = { DLANG(DISPLAY, FAST), DLANG(DISPLAY, ACCURATE) }; char* interpChoices[2] = { DLANG(DISPLAY, FAST), DLANG(DISPLAY, ACCURATE) };
struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(body, DLANG(DISPLAY, INTERPOLATION), interpChoices, 2, &configInterpolationMode, NULL); struct DjuiSelectionbox* selectionbox1 = djui_selectionbox_create(body, DLANG(DISPLAY, INTERPOLATION), interpChoices, 2, &configInterpolationMode, NULL);
djui_base_set_enabled(&selectionbox1->base, (configFrameLimit > 30 || (configFrameLimit <= 30 && configUncappedFramerate))); djui_base_set_enabled(&selectionbox1->base, (configFrameLimit > 30 || configFramerateMode != RRM_MANUAL));
sInterpolationSelectionBox = selectionbox1; sInterpolationSelectionBox = selectionbox1;
char* filterChoices[3] = { DLANG(DISPLAY, NEAREST), DLANG(DISPLAY, LINEAR), DLANG(DISPLAY, TRIPOINT) }; char* filterChoices[3] = { DLANG(DISPLAY, NEAREST), DLANG(DISPLAY, LINEAR), DLANG(DISPLAY, TRIPOINT) };

View file

@ -71,6 +71,10 @@
#include <windows.h> #include <windows.h>
#endif #endif
#ifdef HAVE_SDL2
#include <SDL2/SDL.h>
#endif
extern Vp D_8032CF00; extern Vp D_8032CF00;
OSMesg D_80339BEC; OSMesg D_80339BEC;
@ -187,24 +191,42 @@ static void compute_fps(f64 curTime) {
sDrawnFrames = 0; sDrawnFrames = 0;
} }
static s32 get_num_frames_to_draw(f64 t) { static s32 get_num_frames_to_draw(f64 t, u32 frameLimit) {
if (configFrameLimit % FRAMERATE == 0) { if (frameLimit % FRAMERATE == 0) {
return configFrameLimit / FRAMERATE; return frameLimit / FRAMERATE;
} }
s64 numFramesCurr = (s64) (t * (f64) configFrameLimit); s64 numFramesCurr = (s64) (t * (f64) frameLimit);
s64 numFramesNext = (s64) ((t + sFrameTime) * (f64) configFrameLimit); s64 numFramesNext = (s64) ((t + sFrameTime) * (f64) frameLimit);
return (s32) MAX(1, numFramesNext - numFramesCurr); return (s32) MAX(1, numFramesNext - numFramesCurr);
} }
static u32 get_refresh_rate() {
if (configFramerateMode == RRM_MANUAL) { return configFrameLimit; }
if (configFramerateMode == RRM_UNLIMITED) { return 3000; } // Has no effect
#ifdef HAVE_SDL2
static u32 refreshRate = 60;
if (!refreshRate) {
SDL_DisplayMode mode;
if (SDL_GetCurrentDisplayMode(0, &mode) == 0) {
refreshRate = (u32) mode.refresh_rate;
}
}
return refreshRate;
#else
return 60;
#endif
}
void produce_interpolation_frames_and_delay(void) { void produce_interpolation_frames_and_delay(void) {
bool is30Fps = (!configUncappedFramerate && configFrameLimit == FRAMERATE); u32 refreshRate = get_refresh_rate();
bool is30Fps = (refreshRate == FRAMERATE);
gRenderingInterpolated = true; gRenderingInterpolated = true;
f64 curTime = clock_elapsed_f64();
f64 targetTime = sFrameTimeStart + sFrameTime; f64 targetTime = sFrameTimeStart + sFrameTime;
s32 numFramesToDraw = get_num_frames_to_draw(sFrameTimeStart); s32 numFramesToDraw = get_num_frames_to_draw(sFrameTimeStart, refreshRate);
f64 curTime = clock_elapsed_f64();
f64 loopStartTime = curTime; f64 loopStartTime = curTime;
f64 expectedTime = 0; f64 expectedTime = 0;