From 09b0a12b0aa4bc94d0291524cb5d4282e2e86d5d Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Thu, 21 Nov 2024 02:54:23 -0500 Subject: [PATCH 01/12] Allow changing video resolution at runtime + more Fullscreen exclusive mode is togglable at runtime, as well, as a dropdown menu with "borderless" and "exclusive" options. All variables pertaining to video resolution, exclusive mode, and maximized state are now synchronized. For instance, Alt+Enter will affect the state of the fullscreen video option and maximizing the window via the title bar will update the maximize window option accordingly. --- port/fast3d/gfx_modes.h | 8 +++ port/fast3d/gfx_sdl2.cpp | 98 ++++++++++++++++++++++++++-- port/fast3d/gfx_window_manager_api.h | 7 ++ port/include/video.h | 10 +++ port/src/main.c | 1 + port/src/optionsmenu.c | 79 +++++++++++++++++++--- port/src/video.c | 85 ++++++++++++++++++++++++ 7 files changed, 273 insertions(+), 15 deletions(-) create mode 100644 port/fast3d/gfx_modes.h diff --git a/port/fast3d/gfx_modes.h b/port/fast3d/gfx_modes.h new file mode 100644 index 000000000..1fe895c41 --- /dev/null +++ b/port/fast3d/gfx_modes.h @@ -0,0 +1,8 @@ +#ifndef GFX_MODES_H +#define GFX_MODES_H + +struct GfxModes { + char mode[10]; +}; + +#endif diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index c6b67eebb..d838532b5 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -6,6 +6,7 @@ #include "platform.h" #include "system.h" +#include "gfx_modes.h" #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" @@ -30,10 +31,30 @@ static uint64_t qpc_freq; #define FRAME_INTERVAL_US_NUMERATOR 1000000 #define FRAME_INTERVAL_US_DENOMINATOR (target_fps) -static void set_fullscreen(bool on, bool call_callback) { - if (fullscreen_state == on) { - return; +static int32_t gfx_sdl_get_maximized_state(void) { + return (int32_t)maximized_state; +} + +static int32_t gfx_sdl_get_fullscreen_state(void) { + return (int32_t)fullscreen_state; +} + +static int32_t gfx_sdl_get_fullscreen_flag_mode(void) { + return fullscreen_flag == SDL_WINDOW_FULLSCREEN_DESKTOP ? 0 : 1; +} + +static void gfx_sdl_set_fullscreen_flag(int32_t mode) { + switch (mode) { + case 0: { + fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; + } break; + case 1: { + fullscreen_flag = SDL_WINDOW_FULLSCREEN; + } break; } +} + +static void set_fullscreen(bool on, bool call_callback) { fullscreen_state = on; SDL_SetWindowFullscreen(wnd, on ? fullscreen_flag : 0); if (call_callback && on_fullscreen_changed_callback) { @@ -42,9 +63,6 @@ static void set_fullscreen(bool on, bool call_callback) { } static void set_maximize_window(bool on) { - if (maximized_state == on) { - return; - } maximized_state = on; if (on) { SDL_MaximizeWindow(wnd); @@ -217,6 +235,20 @@ static void gfx_sdl_set_cursor_visibility(bool visible) { } } +static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height) { + const SDL_DisplayMode mode = {.w = width, .h = height}; + SDL_DisplayMode closest = {}; + if (SDL_GetClosestDisplayMode(0, &mode, &closest)) { + SDL_SetWindowDisplayMode(wnd, &closest); + SDL_SetWindowSize(wnd, closest.w, closest.h); + } +} + +static void gfx_sdl_set_dimensions(uint32_t width, uint32_t height, int32_t posX, int32_t posY) { + SDL_SetWindowSize(wnd, width, height); + SDL_SetWindowPosition(wnd, posX, posY); +} + static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) { SDL_GL_GetDrawableSize(wnd, static_cast((void*)width), static_cast((void*)height)); SDL_GetWindowPosition(wnd, static_cast(posX), static_cast(posY)); @@ -235,6 +267,9 @@ static void gfx_sdl_handle_events(void) { case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); + if (!fullscreen_state) { + maximized_state = SDL_GetWindowFlags(wnd) & SDL_WINDOW_MAXIMIZED ? true : false; + } } else if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(wnd)) { // We listen specifically for main window close because closing main window @@ -324,15 +359,63 @@ static bool gfx_sdl_set_swap_interval(int interval) { return success; } -struct GfxWindowManagerAPI gfx_sdl = { +static GfxModes *gfx_sdl_alloc_display_modes(GfxModes *modes, int *num_modes) { + static const char *alloc_failed_msg = "failed to allocate video display modes"; + const int disp_idx = SDL_GetWindowDisplayIndex(wnd); + const int display_modes = SDL_GetNumDisplayModes(disp_idx); + int modes_sz = sizeof *modes * 16; + + modes = (GfxModes *)malloc(modes_sz); + if (!modes) { + sysLogPrintf(LOG_ERROR, "malloc: %s", alloc_failed_msg); + } + + strncpy(modes[0].mode, "Custom", sizeof modes[0].mode); + modes[0].mode[sizeof modes[0].mode - 1] = '\0'; + + int j = 1; + for (int i = 0; i < display_modes; ++i) { + SDL_DisplayMode mode = {}; + if (SDL_GetDisplayMode(disp_idx, i, &mode) == 0) { + if (j == (int)(modes_sz / sizeof *modes)) { + modes = (GfxModes *)realloc(modes, modes_sz <<= 1); + if (!modes) { + sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg); + } + } + + snprintf(modes[j].mode, sizeof modes[0].mode, "%dx%d", mode.w, mode.h); + + if (j && strcmp(modes[j].mode, modes[j - 1].mode) != 0) { + ++j; + } + } + } + + *num_modes = j; + modes_sz = *num_modes * sizeof *modes; + modes = (GfxModes *)realloc(modes, modes_sz); + if (!modes) { + sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg); + } + return modes; +} + +struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, gfx_sdl_close, + gfx_sdl_get_fullscreen_state, gfx_sdl_set_fullscreen_changed_callback, gfx_sdl_set_fullscreen, gfx_sdl_set_fullscreen_exclusive, + gfx_sdl_set_fullscreen_flag, + gfx_sdl_get_fullscreen_flag_mode, + gfx_sdl_get_maximized_state, gfx_sdl_set_maximize_window, gfx_sdl_get_active_window_refresh_rate, gfx_sdl_set_cursor_visibility, + gfx_sdl_set_closest_resolution, + gfx_sdl_set_dimensions, gfx_sdl_get_dimensions, gfx_sdl_handle_events, gfx_sdl_start_frame, @@ -344,4 +427,5 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_get_window_handle, gfx_sdl_set_window_title, gfx_sdl_set_swap_interval, + gfx_sdl_alloc_display_modes, }; diff --git a/port/fast3d/gfx_window_manager_api.h b/port/fast3d/gfx_window_manager_api.h index 61b05315f..ba70a056b 100644 --- a/port/fast3d/gfx_window_manager_api.h +++ b/port/fast3d/gfx_window_manager_api.h @@ -19,12 +19,18 @@ struct GfxWindowInitSettings { struct GfxWindowManagerAPI { void (*init)(const struct GfxWindowInitSettings *settings); void (*close)(void); + int32_t (*get_fullscreen_state)(void); void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); void (*set_fullscreen)(bool enable); void (*set_fullscreen_exclusive)(bool exc); + void (*set_fullscreen_flag)(int32_t mode); + int32_t (*get_fullscreen_flag_mode)(void); + int32_t (*get_maximized_state)(void); void (*set_maximize)(bool enable); void (*get_active_window_refresh_rate)(uint32_t* refresh_rate); void (*set_cursor_visibility)(bool visible); + void (*set_closest_resolution)(int32_t width, int32_t height); + void (*set_dimensions)(uint32_t width, uint32_t height, int32_t posX, int32_t posY); void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY); void (*handle_events)(void); bool (*start_frame)(void); @@ -36,6 +42,7 @@ struct GfxWindowManagerAPI { void *(*get_window_handle)(void); void (*set_window_title)(const char *); bool (*set_swap_interval)(int); + struct GfxModes *(*alloc_display_modes)(struct GfxModes *, int *); }; #endif diff --git a/port/include/video.h b/port/include/video.h index d7a888886..ce0bc2caf 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -27,17 +27,25 @@ s32 videoGetWidth(void); s32 videoGetHeight(void); f32 videoGetAspect(void); s32 videoGetFullscreen(void); +s32 videoGetFullscreenMode(void); s32 videoGetMaximizeWindow(void); void videoSetMaximizeWindow(s32 fs); u32 videoGetTextureFilter(void); s32 videoGetTextureFilter2D(void); s32 videoGetDetailTextures(void); +s32 videoGetDisplayModeIndex(void); +struct GfxModes *videoGetDisplayModes(void); +s32 videoGetNumDisplayModes(void); + +void videoAllocDisplayModes(void); void videoSetWindowOffset(s32 x, s32 y); void videoSetFullscreen(s32 fs); +void videoSetFullscreenMode(s32 mode); void videoSetTextureFilter(u32 filter); void videoSetTextureFilter2D(s32 filter); void videoSetDetailTextures(s32 detail); +void videoSetDisplayMode(const s32 mode_idx); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); @@ -49,4 +57,6 @@ s32 videoFramebuffersSupported(void); void videoResetTextureCache(void); void videoFreeCachedTexture(const void *texptr); +void videoShutdown(void); + #endif diff --git a/port/src/main.c b/port/src/main.c index 5e404426d..f8f6af13f 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -88,6 +88,7 @@ static void cleanup(void) sysLogPrintf(LOG_NOTE, "shutdown"); inputSaveBinds(); configSave(CONFIG_PATH); + videoShutdown(); crashShutdown(); // TODO: actually shut down all subsystems } diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 2b845ebe0..d9e8f75a3 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -9,6 +9,7 @@ #include "game/mainmenu.h" #include "game/menu.h" #include "game/gamefile.h" +#include "../fast3d/gfx_modes.h" #include "video.h" #include "input.h" #include "config.h" @@ -687,6 +688,29 @@ static MenuItemHandlerResult menuhandlerFullScreen(s32 operation, struct menuite return 0; } +static MenuItemHandlerResult menuhandlerFullScreenMode(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *opts[] = { + "Borderless", + "Exclusive" + }; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(opts); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)opts[data->dropdown.value]; + case MENUOP_SET: + videoSetFullscreenMode(data->dropdown.value); + break; + case MENUOP_GETSELECTEDINDEX: + data->dropdown.value = videoGetFullscreenMode(); + } + + return 0; +} + static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { @@ -700,6 +724,29 @@ static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct men return 0; } +static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_CHECKDISABLED: + if (videoGetFullscreen() && videoGetFullscreenMode() == 0) { + return true; + } + break; + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = videoGetNumDisplayModes(); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)videoGetDisplayModes()[data->dropdown.value].mode; + case MENUOP_SET: + videoSetDisplayMode(data->dropdown.value); + break; + case MENUOP_GETSELECTEDINDEX: + data->dropdown.value = videoGetDisplayModeIndex(); + } + + return 0; +} + static MenuItemHandlerResult menuhandlerTexFilter(s32 operation, struct menuitem *item, union handlerdata *data) { static const char *opts[] = { @@ -820,6 +867,30 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerFullScreen, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Full Screen Mode", + 0, + menuhandlerFullScreenMode, + }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Resolution", + 0, + menuhandlerResolution, + }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Texture Filtering", + 0, + menuhandlerTexFilter, + }, { MENUITEMTYPE_CHECKBOX, 0, @@ -836,14 +907,6 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerTexDetail, }, - { - MENUITEMTYPE_DROPDOWN, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Texture Filtering", - 0, - menuhandlerTexFilter, - }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/port/src/video.c b/port/src/video.c index 52eafc228..8acd6caca 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -6,6 +6,7 @@ #include #include "platform.h" #include "config.h" +#include "../fast3d/gfx_modes.h" #include "video.h" #include "../fast3d/gfx_api.h" @@ -39,6 +40,8 @@ static s32 vidAllowHiDpi = false; static s32 vidVsync = 1; static s32 vidMSAA = 1; static s32 vidFramerateLimit = 0; +static struct GfxModes *vidModes = NULL; // Allocated in videoAllocDisplayModes(). Freed in videoShutdown(). +static s32 vidNumModes = 0; static s32 texFilter = FILTER_LINEAR; static s32 texFilter2D = true; @@ -78,6 +81,7 @@ s32 videoInit(void) }; gfx_init(&set); + videoAllocDisplayModes(); if (!wmAPI->set_swap_interval(vidVsync)) { vidVsync = 0; @@ -177,11 +181,19 @@ s32 videoGetHeight(void) s32 videoGetFullscreen(void) { + vidFullscreen = wmAPI->get_fullscreen_state(); return vidFullscreen; } +s32 videoGetFullscreenMode(void) +{ + vidFullscreenExclusive = wmAPI->get_fullscreen_flag_mode(); + return vidFullscreenExclusive; +} + s32 videoGetMaximizeWindow(void) { + vidMaximize = wmAPI->get_maximized_state(); return vidMaximize; } @@ -190,6 +202,59 @@ f32 videoGetAspect(void) return gfx_current_dimensions.aspect_ratio; } +s32 videoGetDisplayModeIndex(void) +{ + for (int i = 1; i < vidNumModes; ++i) { + char tmp[10] = {0}; + snprintf(tmp, sizeof tmp, "%dx%d", gfx_current_dimensions.width, gfx_current_dimensions.height); + if (strcmp(tmp, vidModes[i].mode) == 0) { + return i; + } + } + + // Current dimensions don't match any known mode, so return index 0, "Custom". + return 0; +} + +void videoAllocDisplayModes(void) +{ + vidModes = wmAPI->alloc_display_modes(vidModes, &vidNumModes); +} + +struct GfxModes *videoGetDisplayModes(void) +{ + return vidModes; +} + +s32 videoGetNumDisplayModes(void) +{ + return vidNumModes; +} + +void videoSetDisplayMode(const s32 mode_idx) +{ + const char *cur_mode = vidModes[mode_idx].mode; + char *x_pos = NULL; + + if (mode_idx == 0) { + // "Custom" video mode. + return; + } + + vidWidth = strtol(cur_mode, &x_pos, 10); + cur_mode = x_pos + 1; + vidHeight = strtol(cur_mode, NULL, 10); + + if (vidFullscreen) { + wmAPI->set_closest_resolution(vidWidth, vidHeight); + } else { + if (vidMaximize) { + videoSetMaximizeWindow(false); + } + wmAPI->set_dimensions(vidWidth, vidHeight, 100, 100); + } +} + s32 videoGetTextureFilter2D(void) { return texFilter2D; @@ -215,7 +280,22 @@ void videoSetFullscreen(s32 fs) { if (fs != vidFullscreen) { vidFullscreen = !!fs; + wmAPI->set_closest_resolution(vidWidth, vidHeight); wmAPI->set_fullscreen(vidFullscreen); + if (!vidFullscreen && vidMaximize) { + wmAPI->set_maximize(false); + wmAPI->set_maximize(true); + } + } +} + +void videoSetFullscreenMode(s32 mode) +{ + vidFullscreenExclusive = mode; + wmAPI->set_fullscreen_flag(mode); + if (vidFullscreen) { + wmAPI->set_fullscreen(false); + wmAPI->set_fullscreen(true); } } @@ -287,6 +367,11 @@ void videoFreeCachedTexture(const void *texptr) gfx_texture_cache_delete(texptr); } +void videoShutdown(void) +{ + free(vidModes); +} + PD_CONSTRUCTOR static void videoConfigInit(void) { configRegisterInt("Video.DefaultFullscreen", &vidFullscreen, 0, 1); From 1be0e3680b41b726b533d939a96848b09fa8e503 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Thu, 21 Nov 2024 07:23:54 -0500 Subject: [PATCH 02/12] Add video option to center the window --- port/fast3d/gfx_sdl2.cpp | 35 +++++++++++++++++++++++-- port/fast3d/gfx_window_manager_api.h | 4 ++- port/include/video.h | 2 ++ port/src/optionsmenu.c | 29 ++++++++++++++++++--- port/src/video.c | 38 +++++++++++++++++++++++++--- 5 files changed, 98 insertions(+), 10 deletions(-) diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index d838532b5..e69615953 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -114,6 +114,13 @@ static void gfx_sdl_init(const struct GfxWindowInitSettings *set) { posY = 100; } + if (set->centered) { + SDL_DisplayMode mode = {}; + SDL_GetCurrentDisplayMode(0, &mode); + posX = mode.w / 2 - window_width / 2; + posY = mode.h / 2 - window_height / 2; + } + if (set->fullscreen_is_exclusive) { fullscreen_flag = SDL_WINDOW_FULLSCREEN; } @@ -235,12 +242,35 @@ static void gfx_sdl_set_cursor_visibility(bool visible) { } } -static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height) { +static void get_centered_positions_native(int32_t width, int32_t height, int32_t *posX, int32_t *posY) { + const int disp_idx = SDL_GetWindowDisplayIndex(wnd); + SDL_DisplayMode mode = {}; + SDL_GetDesktopDisplayMode(disp_idx, &mode); + *posX = mode.w / 2 - width / 2; + *posY = mode.h / 2 - height / 2; +} + +static void gfx_sdl_get_centered_positions(int32_t width, int32_t height, int32_t *posX, int32_t *posY) { + const int disp_idx = SDL_GetWindowDisplayIndex(wnd); + SDL_DisplayMode mode = {}; + SDL_GetCurrentDisplayMode(disp_idx, &mode); + *posX = mode.w / 2 - width / 2; + *posY = mode.h / 2 - height / 2; +} + +static void gfx_sdl_set_closest_resolution(int32_t width, int32_t height, bool should_center) { const SDL_DisplayMode mode = {.w = width, .h = height}; + const int disp_idx = SDL_GetWindowDisplayIndex(wnd); SDL_DisplayMode closest = {}; - if (SDL_GetClosestDisplayMode(0, &mode, &closest)) { + if (SDL_GetClosestDisplayMode(disp_idx, &mode, &closest)) { SDL_SetWindowDisplayMode(wnd, &closest); SDL_SetWindowSize(wnd, closest.w, closest.h); + if (should_center) { + int32_t posX = 0; + int32_t posY = 0; + get_centered_positions_native(closest.w, closest.h, &posX, &posY); + SDL_SetWindowPosition(wnd, posX, posY); + } } } @@ -417,6 +447,7 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_set_closest_resolution, gfx_sdl_set_dimensions, gfx_sdl_get_dimensions, + gfx_sdl_get_centered_positions, gfx_sdl_handle_events, gfx_sdl_start_frame, gfx_sdl_swap_buffers_begin, diff --git a/port/fast3d/gfx_window_manager_api.h b/port/fast3d/gfx_window_manager_api.h index ba70a056b..0dc2ddd9a 100644 --- a/port/fast3d/gfx_window_manager_api.h +++ b/port/fast3d/gfx_window_manager_api.h @@ -13,6 +13,7 @@ struct GfxWindowInitSettings { bool fullscreen; bool fullscreen_is_exclusive; bool maximized; + bool centered; bool allow_hidpi; }; @@ -29,9 +30,10 @@ struct GfxWindowManagerAPI { void (*set_maximize)(bool enable); void (*get_active_window_refresh_rate)(uint32_t* refresh_rate); void (*set_cursor_visibility)(bool visible); - void (*set_closest_resolution)(int32_t width, int32_t height); + void (*set_closest_resolution)(int32_t width, int32_t height, bool should_center); void (*set_dimensions)(uint32_t width, uint32_t height, int32_t posX, int32_t posY); void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY); + void (*get_centered_positions)(int32_t width, int32_t height, int32_t *posX, int32_t *posY); void (*handle_events)(void); bool (*start_frame)(void); void (*swap_buffers_begin)(void); diff --git a/port/include/video.h b/port/include/video.h index ce0bc2caf..5c22f466e 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -30,6 +30,8 @@ s32 videoGetFullscreen(void); s32 videoGetFullscreenMode(void); s32 videoGetMaximizeWindow(void); void videoSetMaximizeWindow(s32 fs); +s32 videoGetCenterWindow(void); +void videoSetCenterWindow(s32 center); u32 videoGetTextureFilter(void); s32 videoGetTextureFilter2D(void); s32 videoGetDetailTextures(void); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index d9e8f75a3..12e068ea1 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -724,14 +724,27 @@ static MenuItemHandlerResult menuhandlerMaximizeWindow(s32 operation, struct men return 0; } +static MenuItemHandlerResult menuhandlerCenterWindow(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return videoGetCenterWindow(); + case MENUOP_SET: + videoSetCenterWindow(data->checkbox.value); + break; + } + + return 0; +} + static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { case MENUOP_CHECKDISABLED: - if (videoGetFullscreen() && videoGetFullscreenMode() == 0) { - return true; - } - break; + if (videoGetFullscreen() && videoGetFullscreenMode() == 0) { + return true; + } + break; case MENUOP_GETOPTIONCOUNT: data->dropdown.value = videoGetNumDisplayModes(); break; @@ -899,6 +912,14 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerMaximizeWindow, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Center Window", + 0, + menuhandlerCenterWindow, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/port/src/video.c b/port/src/video.c index 8acd6caca..238fcc386 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -36,6 +36,7 @@ static s32 vidFramebuffers = true; static s32 vidFullscreen = DEFAULT_VID_FULLSCREEN; static s32 vidFullscreenExclusive = DEFAULT_VID_FULLSCREEN_EXCLUSIVE; static s32 vidMaximize = false; +static s32 vidCenter = false; static s32 vidAllowHiDpi = false; static s32 vidVsync = 1; static s32 vidMSAA = 1; @@ -76,6 +77,7 @@ s32 videoInit(void) .fullscreen = vidFullscreen, .fullscreen_is_exclusive = vidFullscreenExclusive, .maximized = vidMaximize, + .centered = vidCenter, .allow_hidpi = vidAllowHiDpi } }; @@ -197,6 +199,11 @@ s32 videoGetMaximizeWindow(void) return vidMaximize; } +s32 videoGetCenterWindow(void) +{ + return vidCenter; +} + f32 videoGetAspect(void) { return gfx_current_dimensions.aspect_ratio; @@ -245,13 +252,20 @@ void videoSetDisplayMode(const s32 mode_idx) cur_mode = x_pos + 1; vidHeight = strtol(cur_mode, NULL, 10); + s32 posX = 100; + s32 posY = 100; + if (vidCenter) { + wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY); + } + if (vidFullscreen) { - wmAPI->set_closest_resolution(vidWidth, vidHeight); + wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter); } else { if (vidMaximize) { videoSetMaximizeWindow(false); + } else { + wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY); } - wmAPI->set_dimensions(vidWidth, vidHeight, 100, 100); } } @@ -280,7 +294,7 @@ void videoSetFullscreen(s32 fs) { if (fs != vidFullscreen) { vidFullscreen = !!fs; - wmAPI->set_closest_resolution(vidWidth, vidHeight); + wmAPI->set_closest_resolution(vidWidth, vidHeight, vidCenter); wmAPI->set_fullscreen(vidFullscreen); if (!vidFullscreen && vidMaximize) { wmAPI->set_maximize(false); @@ -304,6 +318,23 @@ void videoSetMaximizeWindow(s32 fs) if (fs != vidMaximize) { vidMaximize = !!fs; wmAPI->set_maximize(vidMaximize); + if (vidCenter && !vidMaximize) { + s32 posX = 0; + s32 posY = 0; + wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY); + wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY); + } + } +} + +void videoSetCenterWindow(s32 center) +{ + vidCenter = center; + if (vidCenter && !vidMaximize) { + s32 posX = 0; + s32 posY = 0; + wmAPI->get_centered_positions(vidWidth, vidHeight, &posX, &posY); + wmAPI->set_dimensions(vidWidth, vidHeight, posX, posY); } } @@ -379,6 +410,7 @@ PD_CONSTRUCTOR static void videoConfigInit(void) configRegisterInt("Video.DefaultWidth", &vidWidth, 0, 32767); configRegisterInt("Video.DefaultHeight", &vidHeight, 0, 32767); configRegisterInt("Video.ExclusiveFullscreen", &vidFullscreenExclusive, 0, 1); + configRegisterInt("Video.CenterWindow", &vidCenter, 0, 1); configRegisterInt("Video.AllowHiDpi", &vidAllowHiDpi, 0, 1); configRegisterInt("Video.VSync", &vidVsync, -1, 10); configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1); From 5aa1cc611734818573886b1ecb8e83517082e13a Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 23 Nov 2024 02:19:57 -0500 Subject: [PATCH 03/12] Cache ints instead of strings for video modes --- port/fast3d/gfx_modes.h | 8 --- port/fast3d/gfx_sdl2.cpp | 62 ++++++++------------ port/fast3d/gfx_window_manager_api.h | 4 +- port/include/video.h | 11 ++-- port/src/optionsmenu.c | 13 ++++- port/src/video.c | 87 +++++++++++++++++++++------- 6 files changed, 112 insertions(+), 73 deletions(-) delete mode 100644 port/fast3d/gfx_modes.h diff --git a/port/fast3d/gfx_modes.h b/port/fast3d/gfx_modes.h deleted file mode 100644 index 1fe895c41..000000000 --- a/port/fast3d/gfx_modes.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef GFX_MODES_H -#define GFX_MODES_H - -struct GfxModes { - char mode[10]; -}; - -#endif diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index e69615953..5ddacd407 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -6,7 +6,6 @@ #include "platform.h" #include "system.h" -#include "gfx_modes.h" #include "gfx_window_manager_api.h" #include "gfx_screen_config.h" @@ -389,51 +388,39 @@ static bool gfx_sdl_set_swap_interval(int interval) { return success; } -static GfxModes *gfx_sdl_alloc_display_modes(GfxModes *modes, int *num_modes) { - static const char *alloc_failed_msg = "failed to allocate video display modes"; - const int disp_idx = SDL_GetWindowDisplayIndex(wnd); - const int display_modes = SDL_GetNumDisplayModes(disp_idx); - int modes_sz = sizeof *modes * 16; - - modes = (GfxModes *)malloc(modes_sz); - if (!modes) { - sysLogPrintf(LOG_ERROR, "malloc: %s", alloc_failed_msg); +int gfx_sdl_get_display_mode(int modenum, int *out_w, int *out_h) { + const int display_in_use = SDL_GetWindowDisplayIndex(wnd); + SDL_DisplayMode sdlmode; + if (SDL_GetDisplayMode(display_in_use, modenum, &sdlmode) == 0) { + *out_w = sdlmode.w; + *out_h = sdlmode.h; + return 1; } + return 0; +} - strncpy(modes[0].mode, "Custom", sizeof modes[0].mode); - modes[0].mode[sizeof modes[0].mode - 1] = '\0'; - - int j = 1; - for (int i = 0; i < display_modes; ++i) { - SDL_DisplayMode mode = {}; - if (SDL_GetDisplayMode(disp_idx, i, &mode) == 0) { - if (j == (int)(modes_sz / sizeof *modes)) { - modes = (GfxModes *)realloc(modes, modes_sz <<= 1); - if (!modes) { - sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg); - } - } - - snprintf(modes[j].mode, sizeof modes[0].mode, "%dx%d", mode.w, mode.h); - - if (j && strcmp(modes[j].mode, modes[j - 1].mode) != 0) { - ++j; - } - } +int gfx_sdl_get_current_display_mode(int *out_w, int *out_h) { + const int display_in_use = SDL_GetWindowDisplayIndex(wnd); + SDL_DisplayMode sdlmode; + if (SDL_GetCurrentDisplayMode(display_in_use, &sdlmode) == 0) { + *out_w = sdlmode.w; + *out_h = sdlmode.h; + return 1; } + return 0; +} - *num_modes = j; - modes_sz = *num_modes * sizeof *modes; - modes = (GfxModes *)realloc(modes, modes_sz); - if (!modes) { - sysLogPrintf(LOG_ERROR, "realloc: %s", alloc_failed_msg); - } - return modes; +int gfx_sdl_get_num_display_modes(void) { + const int display_in_use = SDL_GetWindowDisplayIndex(wnd); + return SDL_GetNumDisplayModes(display_in_use); } struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, gfx_sdl_close, + gfx_sdl_get_display_mode, + gfx_sdl_get_current_display_mode, + gfx_sdl_get_num_display_modes, gfx_sdl_get_fullscreen_state, gfx_sdl_set_fullscreen_changed_callback, gfx_sdl_set_fullscreen, @@ -458,5 +445,4 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_get_window_handle, gfx_sdl_set_window_title, gfx_sdl_set_swap_interval, - gfx_sdl_alloc_display_modes, }; diff --git a/port/fast3d/gfx_window_manager_api.h b/port/fast3d/gfx_window_manager_api.h index 0dc2ddd9a..4883a79cc 100644 --- a/port/fast3d/gfx_window_manager_api.h +++ b/port/fast3d/gfx_window_manager_api.h @@ -20,6 +20,9 @@ struct GfxWindowInitSettings { struct GfxWindowManagerAPI { void (*init)(const struct GfxWindowInitSettings *settings); void (*close)(void); + int (*get_display_mode)(int modenum, int *out_w, int *out_h); + int (*get_current_display_mode)(int *out_w, int *out_h); + int (*get_num_display_modes)(void); int32_t (*get_fullscreen_state)(void); void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); void (*set_fullscreen)(bool enable); @@ -44,7 +47,6 @@ struct GfxWindowManagerAPI { void *(*get_window_handle)(void); void (*set_window_title)(const char *); bool (*set_swap_interval)(int); - struct GfxModes *(*alloc_display_modes)(struct GfxModes *, int *); }; #endif diff --git a/port/include/video.h b/port/include/video.h index 5c22f466e..97f9e4082 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -11,6 +11,11 @@ #define VIDEO_MAX_FPS 240 #endif +typedef struct { + s32 width; + s32 height; +} displaymode; + s32 videoInit(void); void videoStartFrame(void); void videoSubmitCommands(Gfx *cmds); @@ -36,18 +41,16 @@ u32 videoGetTextureFilter(void); s32 videoGetTextureFilter2D(void); s32 videoGetDetailTextures(void); s32 videoGetDisplayModeIndex(void); -struct GfxModes *videoGetDisplayModes(void); +s32 videoGetDisplayMode(displaymode *out, const s32 index); s32 videoGetNumDisplayModes(void); -void videoAllocDisplayModes(void); - void videoSetWindowOffset(s32 x, s32 y); void videoSetFullscreen(s32 fs); void videoSetFullscreenMode(s32 mode); void videoSetTextureFilter(u32 filter); void videoSetTextureFilter2D(s32 filter); void videoSetDetailTextures(s32 detail); -void videoSetDisplayMode(const s32 mode_idx); +void videoSetDisplayMode(const s32 index); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 12e068ea1..b44355a75 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -9,7 +9,6 @@ #include "game/mainmenu.h" #include "game/menu.h" #include "game/gamefile.h" -#include "../fast3d/gfx_modes.h" #include "video.h" #include "input.h" #include "config.h" @@ -739,6 +738,10 @@ static MenuItemHandlerResult menuhandlerCenterWindow(s32 operation, struct menui static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data) { + static char resstring[32]; + static const char *rescustom = "Custom"; + displaymode mode; + switch (operation) { case MENUOP_CHECKDISABLED: if (videoGetFullscreen() && videoGetFullscreenMode() == 0) { @@ -749,7 +752,13 @@ static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuite data->dropdown.value = videoGetNumDisplayModes(); break; case MENUOP_GETOPTIONTEXT: - return (intptr_t)videoGetDisplayModes()[data->dropdown.value].mode; + videoGetDisplayMode(&mode, data->dropdown.value); + if (mode.width == 0 && mode.height == 0) { + return (intptr_t)rescustom; + } else { + snprintf(resstring, sizeof(resstring), "%dx%d", mode.width, mode.height); + } + return (intptr_t)resstring; case MENUOP_SET: videoSetDisplayMode(data->dropdown.value); break; diff --git a/port/src/video.c b/port/src/video.c index 238fcc386..390ab662e 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -6,7 +6,7 @@ #include #include "platform.h" #include "config.h" -#include "../fast3d/gfx_modes.h" +#include "system.h" #include "video.h" #include "../fast3d/gfx_api.h" @@ -41,8 +41,9 @@ static s32 vidAllowHiDpi = false; static s32 vidVsync = 1; static s32 vidMSAA = 1; static s32 vidFramerateLimit = 0; -static struct GfxModes *vidModes = NULL; // Allocated in videoAllocDisplayModes(). Freed in videoShutdown(). -static s32 vidNumModes = 0; +static displaymode vidModeDefault; +static s32 vidNumModes = 1; +static displaymode *vidModes = &vidModeDefault; static s32 texFilter = FILTER_LINEAR; static s32 texFilter2D = true; @@ -53,6 +54,8 @@ static u32 frames = 0; static u32 framesPerSec = 0; static f64 startTime, endTime, fpsTime; +static s32 videoInitDisplayModes(void); + s32 videoInit(void) { wmAPI = &gfx_sdl; @@ -83,7 +86,7 @@ s32 videoInit(void) }; gfx_init(&set); - videoAllocDisplayModes(); + videoInitDisplayModes(); if (!wmAPI->set_swap_interval(vidVsync)) { vidVsync = 0; @@ -211,26 +214,72 @@ f32 videoGetAspect(void) s32 videoGetDisplayModeIndex(void) { - for (int i = 1; i < vidNumModes; ++i) { - char tmp[10] = {0}; - snprintf(tmp, sizeof tmp, "%dx%d", gfx_current_dimensions.width, gfx_current_dimensions.height); - if (strcmp(tmp, vidModes[i].mode) == 0) { + for (s32 i = 1; i < vidNumModes; ++i) { + if (vidModes[i].width == gfx_current_dimensions.width && + vidModes[i].height == gfx_current_dimensions.height) { return i; } } - // Current dimensions don't match any known mode, so return index 0, "Custom". return 0; } -void videoAllocDisplayModes(void) +static s32 videoInitDisplayModes(void) { - vidModes = wmAPI->alloc_display_modes(vidModes, &vidNumModes); + if (!wmAPI->get_current_display_mode(&vidModeDefault.width, &vidModeDefault.height)) { + vidModeDefault.width = 640; + vidModeDefault.height = 480; + return false; + } + + const s32 numBaseModes = wmAPI->get_num_display_modes(); + if (!numBaseModes) { + return false; + } + + const s32 numCustomModes = 1; + displaymode *modeList = sysMemZeroAlloc((numBaseModes + numCustomModes) * sizeof(displaymode)); + if (!modeList) { + return false; + } + + modeList[0].width = 0; + modeList[0].height = 0; + + s32 numModes = 1; + s32 w = -1, h = w, neww = w, newh = w; + + // SDL modes are guaranteed to be sorted high to low + for (s32 i = 0; i < numBaseModes; ++i) { + wmAPI->get_display_mode(i, &neww, &newh); + + if (neww != w || newh != h) { + w = neww; + h = newh; + modeList[numModes].width = w; + modeList[numModes].height = h; + ++numModes; + } + } + + modeList = sysMemRealloc(modeList, numModes * sizeof(displaymode)); + if (!modeList) { + return false; + } + + vidModes = modeList; + vidNumModes = numModes; + + return true; } -struct GfxModes *videoGetDisplayModes(void) +s32 videoGetDisplayMode(displaymode *out, const s32 index) { - return vidModes; + if (index >= 0 && index < vidNumModes) { + *out = vidModes[index]; + return true; + } + return false; } s32 videoGetNumDisplayModes(void) @@ -238,19 +287,17 @@ s32 videoGetNumDisplayModes(void) return vidNumModes; } -void videoSetDisplayMode(const s32 mode_idx) +void videoSetDisplayMode(const s32 index) { - const char *cur_mode = vidModes[mode_idx].mode; - char *x_pos = NULL; + const displaymode dm = vidModes[index]; - if (mode_idx == 0) { + if (index == 0) { // "Custom" video mode. return; } - vidWidth = strtol(cur_mode, &x_pos, 10); - cur_mode = x_pos + 1; - vidHeight = strtol(cur_mode, NULL, 10); + vidWidth = dm.width; + vidHeight = dm.height; s32 posX = 100; s32 posY = 100; From 1d14877bec682e10f4cb82e68c727ff8310af13e Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Tue, 26 Nov 2024 05:43:41 -0500 Subject: [PATCH 04/12] Allow changing vsync and framerate limit at runtime Also implements a new menu item flag: MENUITEMFLAG_SLIDER_DEFERRED, which only calls the MENUOP_GETSLIDER callback on entering dimmed mode, storing its value for later ticks, and only calls the MENUOP_SET callback on leaving dimmed mode. This was necessary to prevent the framerate limit slider from immediately affecting the framerate, leading to an annoying slow down as the value was decremented to 0. --- port/fast3d/gfx_sdl2.cpp | 10 ++++ port/fast3d/gfx_window_manager_api.h | 2 + port/include/video.h | 4 ++ port/src/optionsmenu.c | 70 ++++++++++++++++++++++++++++ port/src/video.c | 41 ++++++++++++---- src/game/menuitem.c | 44 +++++++++++++---- src/include/constants.h | 3 +- 7 files changed, 155 insertions(+), 19 deletions(-) diff --git a/port/fast3d/gfx_sdl2.cpp b/port/fast3d/gfx_sdl2.cpp index 5ddacd407..c1ec8ce6e 100644 --- a/port/fast3d/gfx_sdl2.cpp +++ b/port/fast3d/gfx_sdl2.cpp @@ -363,6 +363,10 @@ static double gfx_sdl_get_time(void) { return SDL_GetPerformanceCounter() / (double)qpc_freq; } +static int32_t gfx_sdl_get_target_fps(void) { + return target_fps; +} + static void gfx_sdl_set_target_fps(int fps) { target_fps = fps; } @@ -379,6 +383,10 @@ static void gfx_sdl_set_window_title(const char *title) { SDL_SetWindowTitle(wnd, title); } +static int gfx_sdl_get_swap_interval(void) { + return SDL_GL_GetSwapInterval(); +} + static bool gfx_sdl_set_swap_interval(int interval) { const bool success = SDL_GL_SetSwapInterval(interval) >= 0; vsync_enabled = success && (interval != 0); @@ -440,9 +448,11 @@ struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_swap_buffers_begin, gfx_sdl_swap_buffers_end, gfx_sdl_get_time, + gfx_sdl_get_target_fps, gfx_sdl_set_target_fps, gfx_sdl_can_disable_vsync, gfx_sdl_get_window_handle, gfx_sdl_set_window_title, + gfx_sdl_get_swap_interval, gfx_sdl_set_swap_interval, }; diff --git a/port/fast3d/gfx_window_manager_api.h b/port/fast3d/gfx_window_manager_api.h index 4883a79cc..d10fc45cf 100644 --- a/port/fast3d/gfx_window_manager_api.h +++ b/port/fast3d/gfx_window_manager_api.h @@ -42,10 +42,12 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug + int32_t (*get_target_fps)(void); void (*set_target_fps)(int fps); bool (*can_disable_vsync)(void); void *(*get_window_handle)(void); void (*set_window_title)(const char *); + int (*get_swap_interval)(void); bool (*set_swap_interval)(int); }; diff --git a/port/include/video.h b/port/include/video.h index 97f9e4082..c99655110 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -43,6 +43,8 @@ s32 videoGetDetailTextures(void); s32 videoGetDisplayModeIndex(void); s32 videoGetDisplayMode(displaymode *out, const s32 index); s32 videoGetNumDisplayModes(void); +s32 videoGetVsync(void); +s32 videoGetFramerateLimit(void); void videoSetWindowOffset(s32 x, s32 y); void videoSetFullscreen(s32 fs); @@ -51,6 +53,8 @@ void videoSetTextureFilter(u32 filter); void videoSetTextureFilter2D(s32 filter); void videoSetDetailTextures(s32 detail); void videoSetDisplayMode(const s32 index); +void videoSetVsync(const s32 vsync); +void videoSetFramerateLimit(const s32 limit); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index b44355a75..022e1e802 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -736,6 +736,60 @@ static MenuItemHandlerResult menuhandlerCenterWindow(s32 operation, struct menui return 0; } +static MenuItemHandlerResult menuhandlerVsync(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *opts[] = { + "Adaptive", + "Off", + "On (Sync Every Frame)", + "On (Sync Every 2 Frames)", + "On (Sync Every 3 Frames)", + "On (Sync Every 4 Frames)", + "On (Sync Every 5 Frames)", + "On (Sync Every 6 Frames)", + "On (Sync Every 7 Frames)", + "On (Sync Every 8 Frames)", + "On (Sync Every 9 Frames)", + "On (Sync Every 10 Frames)" + }; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(opts); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)opts[data->dropdown.value]; + case MENUOP_SET: + videoSetVsync(data->dropdown.value - 1); + case MENUOP_GETSELECTEDINDEX: + data->dropdown.value = videoGetVsync() + 1; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerFramerateLimit(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = videoGetFramerateLimit(); + break; + case MENUOP_SET: + if (g_TickRateDiv < 2) { + g_TickRateDiv = (data->slider.value == 0 || data->slider.value > 60) ? 0 : 1; + } + videoSetFramerateLimit(data->slider.value); + break; + case MENUOP_GETSLIDERLABEL: + if (data->slider.value == 0) { + sprintf(data->slider.label, "Off"); + } + break; + } + + return 0; +} + static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data) { static char resstring[32]; @@ -929,6 +983,22 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerCenterWindow, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Vsync", + 0, + menuhandlerVsync, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE | MENUITEMFLAG_SLIDER_DEFERRED, + (uintptr_t)"Framerate Limit", + VIDEO_MAX_FPS, + menuhandlerFramerateLimit, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/port/src/video.c b/port/src/video.c index 390ab662e..cd1f6288d 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -86,18 +86,11 @@ s32 videoInit(void) }; gfx_init(&set); + videoInitDisplayModes(); + videoSetVsync(vidVsync); + videoSetFramerateLimit(vidFramerateLimit); - if (!wmAPI->set_swap_interval(vidVsync)) { - vidVsync = 0; - } - - if (vidVsync == 0 && vidFramerateLimit == 0) { - // cap FPS if there's no vsync to prevent the game from exploding - vidFramerateLimit = VIDEO_MAX_FPS; - } - - wmAPI->set_target_fps(vidFramerateLimit); // disabled because vsync is on gfx_set_texture_filter((enum FilteringMode)texFilter); initDone = true; @@ -224,6 +217,18 @@ s32 videoGetDisplayModeIndex(void) return 0; } +s32 videoGetVsync(void) +{ + vidVsync = wmAPI->get_swap_interval(); + return vidVsync; +} + +s32 videoGetFramerateLimit(void) +{ + vidFramerateLimit = wmAPI->get_target_fps(); + return vidFramerateLimit; +} + static s32 videoInitDisplayModes(void) { if (!wmAPI->get_current_display_mode(&vidModeDefault.width, &vidModeDefault.height)) { @@ -409,6 +414,22 @@ s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize) return gfx_create_framebuffer(w, h, upscale, autoresize); } +void videoSetVsync(const s32 vsync) +{ + vidVsync = wmAPI->set_swap_interval(vsync) ? vsync : 0; + + if (vidVsync == 0 && vidFramerateLimit == 0) { + // cap FPS if there's no vsync to prevent the game from exploding + videoSetFramerateLimit(VIDEO_MAX_FPS); + } +} + +void videoSetFramerateLimit(const s32 limit) +{ + vidFramerateLimit = (vidVsync == 0 && limit == 0) ? VIDEO_MAX_FPS : limit; + wmAPI->set_target_fps(vidFramerateLimit); +} + void videoSetFramebuffer(s32 target) { return gfx_set_framebuffer(target, 1.f); diff --git a/src/game/menuitem.c b/src/game/menuitem.c index c95acb89e..9a3625030 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -44,6 +44,8 @@ u8 g_KeyboardKeys[5][10] = { { '1','2','1','2','1','2','3','1','2','3' }, }; +static s32 deferredindex = -1; + s32 func0f0e5ce0(s32 value) { if (value < var800711a4) { @@ -2171,7 +2173,7 @@ Gfx* menuitemColorBoxRender(Gfx *gdl, struct menurendercontext *context) return gdl; } -#endif +#endif Gfx *menuitemSelectableRender(Gfx *gdl, struct menurendercontext *context) { @@ -2332,8 +2334,15 @@ Gfx *menuitemSliderRender(Gfx *gdl, struct menurendercontext *context) extray = 0; if (context->item->handler != NULL) { - context->item->handler(MENUOP_GETSLIDER, context->item, &data); - slidervalue = (s16) data.slider.value; + if ((context->item->flags & MENUITEMFLAG_SLIDER_DEFERRED) && + deferredindex != -1 && + context->dialog->dimmed && + context->focused) { + slidervalue = (s16) deferredindex; + } else { + context->item->handler(MENUOP_GETSLIDER, context->item, &data); + slidervalue = (s16) data.slider.value; + } } else { slidervalue = 0; } @@ -2484,8 +2493,17 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct if (tickflags & MENUTICKFLAG_DIALOGISDIMMED) { if (item->handler) { - item->handler(MENUOP_GETSLIDER, item, &handlerdata); - index = (s16) handlerdata.slider.value; + if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) { + if (deferredindex == -1) { + item->handler(MENUOP_GETSLIDER, item, &handlerdata); + deferredindex = (s16) handlerdata.slider.value; + } + index = deferredindex; + } else { + item->handler(MENUOP_GETSLIDER, item, &handlerdata); + index = (s16) handlerdata.slider.value; + } + } else { index = 0; } @@ -2565,11 +2583,21 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct inputs->leftright = 0; handlerdata.slider.value = index; - if (item->handler) { - item->handler(MENUOP_SET, item, &handlerdata); + if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) { + deferredindex = index; + } else { + if (item->handler) { + item->handler(MENUOP_SET, item, &handlerdata); + } } if (inputs->select) { + if (item->flags & MENUITEMFLAG_SLIDER_DEFERRED) { + deferredindex = -1; + if (item->handler) { + item->handler(MENUOP_SET, item, &handlerdata); + } + } dialog->dimmed = false; } } else { @@ -2609,7 +2637,7 @@ Gfx *menuitemCarouselRender(Gfx *gdl, struct menurendercontext *context) #ifdef PLATFORM_N64 s16 chevronOffset = 0; -#else +#else s16 chevronOffset = 3; #endif diff --git a/src/include/constants.h b/src/include/constants.h index 777fbed56..110e88f5b 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -1666,6 +1666,7 @@ #define MENUITEMFLAG_CAROUSEL_04000000 0x04000000 #define MENUITEMFLAG_LITERAL_TEXT 0x08000000 #define MENUITEMFLAG_SLIDER_WIDE 0x10000000 +#define MENUITEMFLAG_SLIDER_DEFERRED 0x20000000 #define MENUITEMTYPE_LABEL 0x01 #define MENUITEMTYPE_LIST 0x02 @@ -1696,7 +1697,7 @@ #ifndef PLATFORM_N64 #define MENUITEMTYPE_COLORBOX 0x1b -#endif +#endif #define MENUMODELFLAG_HASSCALE 0x01 #define MENUMODELFLAG_HASPOSITION 0x02 From d43c4c0cbad3c821494f65b2ae9aaf1468aabae6 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Wed, 27 Nov 2024 04:35:21 -0500 Subject: [PATCH 05/12] Allow changing MSAA at runtime --- port/include/video.h | 2 ++ port/src/optionsmenu.c | 46 ++++++++++++++++++++++++++++++++++++++++++ port/src/video.c | 12 +++++++++++ 3 files changed, 60 insertions(+) diff --git a/port/include/video.h b/port/include/video.h index c99655110..0e4b242e2 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -45,6 +45,7 @@ s32 videoGetDisplayMode(displaymode *out, const s32 index); s32 videoGetNumDisplayModes(void); s32 videoGetVsync(void); s32 videoGetFramerateLimit(void); +s32 videoGetMSAA(void); void videoSetWindowOffset(s32 x, s32 y); void videoSetFullscreen(s32 fs); @@ -55,6 +56,7 @@ void videoSetDetailTextures(s32 detail); void videoSetDisplayMode(const s32 index); void videoSetVsync(const s32 vsync); void videoSetFramerateLimit(const s32 limit); +void videoSetMSAA(const s32 msaa); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 022e1e802..dfc12c97b 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -790,6 +790,44 @@ static MenuItemHandlerResult menuhandlerFramerateLimit(s32 operation, struct men return 0; } +static MenuItemHandlerResult menuhandlerMSAA(s32 operation, struct menuitem *item, union handlerdata *data) +{ + s32 msaa; + static const char *opts[] = { + "Off", + "2x", + "4x", + "8x", + "16x" + }; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(opts); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)opts[data->dropdown.value]; + case MENUOP_SET: + videoSetMSAA(1 << data->dropdown.value); + break; + case MENUOP_GETSELECTEDINDEX: + msaa = videoGetMSAA(); + if (msaa < 2) { + data->dropdown.value = 0; + } else if (msaa < 4) { + data->dropdown.value = 1; + } else if (msaa < 8) { + data->dropdown.value = 2; + } else if (msaa < 16) { + data->dropdown.value = 3; + } else { + data->dropdown.value = 4; + } + } + + return 0; +} + static MenuItemHandlerResult menuhandlerResolution(s32 operation, struct menuitem *item, union handlerdata *data) { static char resstring[32]; @@ -999,6 +1037,14 @@ struct menuitem g_ExtendedVideoMenuItems[] = { VIDEO_MAX_FPS, menuhandlerFramerateLimit, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Anti-aliasing (MSAA)", + 0, + menuhandlerMSAA, + }, { MENUITEMTYPE_CHECKBOX, 0, diff --git a/port/src/video.c b/port/src/video.c index cd1f6288d..0a84df49b 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -217,6 +217,12 @@ s32 videoGetDisplayModeIndex(void) return 0; } +s32 videoGetMSAA(void) +{ + vidMSAA = (s32)gfx_msaa_level; + return vidMSAA; +} + s32 videoGetVsync(void) { vidVsync = wmAPI->get_swap_interval(); @@ -414,6 +420,12 @@ s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize) return gfx_create_framebuffer(w, h, upscale, autoresize); } +void videoSetMSAA(const s32 msaa) +{ + vidMSAA = msaa; + gfx_msaa_level = (u32)vidMSAA; +} + void videoSetVsync(const s32 vsync) { vidVsync = wmAPI->set_swap_interval(vsync) ? vsync : 0; From 6de98cfcd45687375429dc745450ebc8ce139d29 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Wed, 27 Nov 2024 11:53:40 -0500 Subject: [PATCH 06/12] Update GUI texture filtering setting immediately --- port/src/optionsmenu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index dfc12c97b..9e23290af 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -905,6 +905,7 @@ static MenuItemHandlerResult menuhandlerTexFilter2D(s32 operation, struct menuit return videoGetTextureFilter2D(); case MENUOP_SET: videoSetTextureFilter2D(data->checkbox.value); + g_TexFilter2D = videoGetTextureFilter2D() ? G_TF_BILERP : G_TF_POINT; break; } From 210b299bd40719cfd88055df6693141f8a45ad31 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 30 Nov 2024 11:58:14 -0500 Subject: [PATCH 07/12] Add Display FPS options to Extended Video Options Also changed how the window title FPS works, which is now derived from the difference in time between subsequent video frames. --- port/include/video.h | 5 ++++ port/src/optionsmenu.c | 67 ++++++++++++++++++++++++++++++++++++++++++ port/src/video.c | 49 ++++++++++++++++++++++++------ src/game/lv.c | 37 +++++++++++++++++++++++ 4 files changed, 149 insertions(+), 9 deletions(-) diff --git a/port/include/video.h b/port/include/video.h index 0e4b242e2..0c468dcbb 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -21,6 +21,7 @@ void videoStartFrame(void); void videoSubmitCommands(Gfx *cmds); void videoClearScreen(void); void videoEndFrame(void); +f64 videoGetDiffTime(void); void *videoGetWindowHandle(void); @@ -45,6 +46,8 @@ s32 videoGetDisplayMode(displaymode *out, const s32 index); s32 videoGetNumDisplayModes(void); s32 videoGetVsync(void); s32 videoGetFramerateLimit(void); +s32 videoGetDisplayFPS(void); +s32 videoGetDisplayFPSDivisor(void); s32 videoGetMSAA(void); void videoSetWindowOffset(s32 x, s32 y); @@ -56,6 +59,8 @@ void videoSetDetailTextures(s32 detail); void videoSetDisplayMode(const s32 index); void videoSetVsync(const s32 vsync); void videoSetFramerateLimit(const s32 limit); +void videoSetDisplayFPS(const s32 displayfps); +void videoSetDisplayFPSDivisor(const s32 divisor); void videoSetMSAA(const s32 msaa); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 9e23290af..5835ee921 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -912,6 +912,57 @@ static MenuItemHandlerResult menuhandlerTexFilter2D(s32 operation, struct menuit return 0; } +static MenuItemHandlerResult menuhandlerDisplayFPS(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return videoGetDisplayFPS(); + case MENUOP_SET: + videoSetDisplayFPS(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerDisplayFPSInterval(s32 operation, struct menuitem *item, union handlerdata *data) +{ + s32 divisor; + static const char *opts[] = { + "1 Sec", + "1/2 Secs", + "1/4 Secs", + "1/8 Secs", + "1/16 Secs", + }; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = ARRAYCOUNT(opts); + break; + case MENUOP_GETOPTIONTEXT: + return (intptr_t)opts[data->dropdown.value]; + case MENUOP_SET: + videoSetDisplayFPSDivisor(1 << data->dropdown.value); + break; + case MENUOP_GETSELECTEDINDEX: + divisor = videoGetDisplayFPSDivisor(); + if (divisor < 2) { + data->dropdown.value = 0; + } else if (divisor < 4) { + data->dropdown.value = 1; + } else if (divisor < 8) { + data->dropdown.value = 2; + } else if (divisor < 16) { + data->dropdown.value = 3; + } else { + data->dropdown.value = 4; + } + } + + return 0; +} + static MenuItemHandlerResult menuhandlerGeMuzzleFlashes(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { @@ -1038,6 +1089,22 @@ struct menuitem g_ExtendedVideoMenuItems[] = { VIDEO_MAX_FPS, menuhandlerFramerateLimit, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Display FPS", + 0, + menuhandlerDisplayFPS, + }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Display FPS Interval", + 0, + menuhandlerDisplayFPSInterval, + }, { MENUITEMTYPE_DROPDOWN, 0, diff --git a/port/src/video.c b/port/src/video.c index 0a84df49b..726096de4 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -41,9 +41,12 @@ static s32 vidAllowHiDpi = false; static s32 vidVsync = 1; static s32 vidMSAA = 1; static s32 vidFramerateLimit = 0; +static s32 vidDisplayFPS = 0; +static s32 vidDisplayFPSDivisor = 8; static displaymode vidModeDefault; static s32 vidNumModes = 1; static displaymode *vidModes = &vidModeDefault; +static f64 vidDiffTime; static s32 texFilter = FILTER_LINEAR; static s32 texFilter2D = true; @@ -51,8 +54,7 @@ static s32 texDetail = true; static u32 dlcount = 0; static u32 frames = 0; -static u32 framesPerSec = 0; -static f64 startTime, endTime, fpsTime; +static f64 startTime, endTime; static s32 videoInitDisplayModes(void); @@ -115,26 +117,33 @@ void videoSubmitCommands(Gfx *cmds) void videoEndFrame(void) { + static f64 accumTime = 0.0; + if (!initDone) { return; } gfx_end_frame(); - endTime = wmAPI->get_time(); - ++frames; - ++framesPerSec; - if (endTime >= fpsTime) { + endTime = wmAPI->get_time(); + accumTime += endTime - startTime; + + if (accumTime >= 1.0 / (f64)vidDisplayFPSDivisor) { char tmp[128]; - snprintf(tmp, sizeof(tmp), "fps %3u frt %lf frm %u", framesPerSec, endTime - startTime, frames); + vidDiffTime = endTime - startTime; + accumTime = 0.0; + snprintf(tmp, sizeof(tmp), "fps %3u frt %lf frm %u", (u32)(1.0 / vidDiffTime), vidDiffTime, frames); wmAPI->set_window_title(tmp); - framesPerSec = 0; - fpsTime = endTime + 1.0; } } +f64 videoGetDiffTime(void) +{ + return vidDiffTime; +} + void videoClearScreen(void) { videoStartFrame(); @@ -235,6 +244,16 @@ s32 videoGetFramerateLimit(void) return vidFramerateLimit; } +s32 videoGetDisplayFPS(void) +{ + return vidDisplayFPS; +} + +s32 videoGetDisplayFPSDivisor(void) +{ + return vidDisplayFPSDivisor; +} + static s32 videoInitDisplayModes(void) { if (!wmAPI->get_current_display_mode(&vidModeDefault.width, &vidModeDefault.height)) { @@ -442,6 +461,16 @@ void videoSetFramerateLimit(const s32 limit) wmAPI->set_target_fps(vidFramerateLimit); } +void videoSetDisplayFPS(const s32 displayfps) +{ + vidDisplayFPS = displayfps; +} + +void videoSetDisplayFPSDivisor(const s32 divisor) +{ + vidDisplayFPSDivisor = divisor; +} + void videoSetFramebuffer(s32 target) { return gfx_set_framebuffer(target, 1.f); @@ -495,6 +524,8 @@ PD_CONSTRUCTOR static void videoConfigInit(void) configRegisterInt("Video.VSync", &vidVsync, -1, 10); configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1); configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, VIDEO_MAX_FPS); + configRegisterInt("Video.DisplayFPS", &vidDisplayFPS, 0, 1); + configRegisterInt("Video.DisplayFPSDivisor", &vidDisplayFPSDivisor, 1, 16); configRegisterInt("Video.MSAA", &vidMSAA, 1, 16); configRegisterInt("Video.TextureFilter", &texFilter, 0, 2); configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1); diff --git a/src/game/lv.c b/src/game/lv.c index 22f3993a8..06bff3b84 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -96,6 +96,9 @@ #include "lib/vars.h" #include "lib/vi.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "video.h" +#endif struct sndstate *g_MiscSfxAudioHandles[3]; u32 var800aa5bc; @@ -951,6 +954,34 @@ void lvFindThreats(void) } } +#ifndef PLATFORM_N64 +Gfx *lvRenderFPS(Gfx *gdl) +{ + s32 x = 27, y = 13; + f32 fps = 1.0f / videoGetDiffTime(); + s32 ifps = (s32)fps; + u8 r = (ifps <= 30) ? 255 : (ifps <= 60) ? roundf(255 * (1.0f - (ifps - 30.0f) / 30.0f)) : 0; + u8 g = (ifps <= 30) ? roundf(255 * (ifps - 1.0f) / 29.0f) : 255; + u8 b = (ifps > 60 && ifps <= 90) ? roundf(255 * (ifps - 60.0f) / 30.0f) : (ifps > 90) ? 255 : 0; + u8 a = 160; + u32 color = r << 24 | g << 16 | b << 8 | a; + char buffer[16]; + + if (g_CharsNumeric && g_FontNumeric) { + snprintf(buffer, sizeof buffer, "%.2f", fps); + + gSPSetExtraGeometryModeEXT(gdl++, g_HudAlignModeL); + + gdl = text0f153628(gdl); + gdl = textRender(gdl, &x, &y, buffer, g_CharsNumeric, g_FontNumeric, color, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0); + + gSPClearExtraGeometryModeEXT(gdl++, g_HudAlignModeL); + } + + return gdl; +} +#endif + /** * Renders a complete frame for all players, and also does some other game logic * that really doesn't belong here. @@ -1745,6 +1776,12 @@ Gfx *lvRender(Gfx *gdl) gDPSetScissor(gdl++, G_SC_NON_INTERLACE, 0, 0, viGetWidth(), viGetHeight()); +#ifndef PLATFORM_N64 + if (videoGetDisplayFPS()) { + gdl = lvRenderFPS(gdl); + } +#endif + #if VERSION < VERSION_NTSC_1_0 if ((uintptr_t)gdl < (uintptr_t)g_GfxBuffers[g_GfxActiveBufferIndex] || (uintptr_t)gdl > (uintptr_t)g_GfxBuffers[g_GfxActiveBufferIndex + 1]) { From 1e2f6dcb91131eb0da4271d0eb08e8e7ad20b9a3 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 30 Nov 2024 13:58:33 -0500 Subject: [PATCH 08/12] Fix mouse input not updating deferred slider while in dimmed mode --- src/game/menuitem.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/game/menuitem.c b/src/game/menuitem.c index 9a3625030..59eec1160 100644 --- a/src/game/menuitem.c +++ b/src/game/menuitem.c @@ -2481,9 +2481,14 @@ bool menuitemSliderTick(struct menuitem *item, struct menudialog *dialog, struct index = item->param3; } if (item->handler) { - item->handler(MENUOP_GET, item, &handlerdata); - handlerdata.slider.value = index; - item->handler(MENUOP_SET, item, &handlerdata); + if ((item->flags & MENUITEMFLAG_SLIDER_DEFERRED) && + (tickflags & MENUTICKFLAG_DIALOGISDIMMED)) { + deferredindex = index; + } else { + item->handler(MENUOP_GET, item, &handlerdata); + handlerdata.slider.value = index; + item->handler(MENUOP_SET, item, &handlerdata); + } } return true; } From b34bb012409293f1f6f7552ed85a2a5701c9d9a8 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 30 Nov 2024 17:30:59 -0500 Subject: [PATCH 09/12] Actually sync fullscreen and maximized states with the backend --- port/src/video.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/port/src/video.c b/port/src/video.c index 726096de4..9a121421d 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -105,6 +105,10 @@ void videoStartFrame(void) startTime = wmAPI->get_time(); gfx_start_frame(); } + + // Synchronize with their backend counterparts. + vidFullscreen = videoGetFullscreen(); + vidMaximize = videoGetMaximizeWindow(); } void videoSubmitCommands(Gfx *cmds) From 0b8a333e2b8a3350dca22cc31e4108caba236dfa Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 30 Nov 2024 19:45:30 -0500 Subject: [PATCH 10/12] Rearrange the menu, clean up, and change some things --- port/src/optionsmenu.c | 148 ++++++++++++++++++++++------------------- port/src/video.c | 4 +- 2 files changed, 80 insertions(+), 72 deletions(-) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 5835ee921..e26c8efcd 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -741,16 +741,16 @@ static MenuItemHandlerResult menuhandlerVsync(s32 operation, struct menuitem *it static const char *opts[] = { "Adaptive", "Off", - "On (Sync Every Frame)", - "On (Sync Every 2 Frames)", - "On (Sync Every 3 Frames)", - "On (Sync Every 4 Frames)", - "On (Sync Every 5 Frames)", - "On (Sync Every 6 Frames)", - "On (Sync Every 7 Frames)", - "On (Sync Every 8 Frames)", - "On (Sync Every 9 Frames)", - "On (Sync Every 10 Frames)" + "On (Every Frame)", + "On (Every 2 Frames)", + "On (Every 3 Frames)", + "On (Every 4 Frames)", + "On (Every 5 Frames)", + "On (Every 6 Frames)", + "On (Every 7 Frames)", + "On (Every 8 Frames)", + "On (Every 9 Frames)", + "On (Every 10 Frames)" }; switch (operation) { @@ -781,10 +781,12 @@ static MenuItemHandlerResult menuhandlerFramerateLimit(s32 operation, struct men videoSetFramerateLimit(data->slider.value); break; case MENUOP_GETSLIDERLABEL: + // NOTE: data->slider.label length must not exceed 15. if (data->slider.value == 0) { - sprintf(data->slider.label, "Off"); + strcpy(data->slider.label, "Off"); + } else { + sprintf(data->slider.label, "%d FPS", data->slider.value); } - break; } return 0; @@ -795,10 +797,10 @@ static MenuItemHandlerResult menuhandlerMSAA(s32 operation, struct menuitem *ite s32 msaa; static const char *opts[] = { "Off", - "2x", - "4x", - "8x", - "16x" + "2x (MSAA)", + "4x (MSAA)", + "8x (MSAA)", + "16x (MSAA)" }; switch (operation) { @@ -919,7 +921,6 @@ static MenuItemHandlerResult menuhandlerDisplayFPS(s32 operation, struct menuite return videoGetDisplayFPS(); case MENUOP_SET: videoSetDisplayFPS(data->checkbox.value); - break; } return 0; @@ -927,36 +928,19 @@ static MenuItemHandlerResult menuhandlerDisplayFPS(s32 operation, struct menuite static MenuItemHandlerResult menuhandlerDisplayFPSInterval(s32 operation, struct menuitem *item, union handlerdata *data) { - s32 divisor; - static const char *opts[] = { - "1 Sec", - "1/2 Secs", - "1/4 Secs", - "1/8 Secs", - "1/16 Secs", - }; - switch (operation) { - case MENUOP_GETOPTIONCOUNT: - data->dropdown.value = ARRAYCOUNT(opts); + case MENUOP_GETSLIDER: + data->slider.value = videoGetDisplayFPSDivisor() - 1; break; - case MENUOP_GETOPTIONTEXT: - return (intptr_t)opts[data->dropdown.value]; case MENUOP_SET: - videoSetDisplayFPSDivisor(1 << data->dropdown.value); + videoSetDisplayFPSDivisor(data->dropdown.value + 1); break; - case MENUOP_GETSELECTEDINDEX: - divisor = videoGetDisplayFPSDivisor(); - if (divisor < 2) { - data->dropdown.value = 0; - } else if (divisor < 4) { - data->dropdown.value = 1; - } else if (divisor < 8) { - data->dropdown.value = 2; - } else if (divisor < 16) { - data->dropdown.value = 3; + case MENUOP_GETSLIDERLABEL: + // NOTE: data->slider.label length must not exceed 15. + if (data->slider.value == 0) { + strcpy(data->slider.label, "1 Sec"); } else { - data->dropdown.value = 4; + sprintf(data->slider.label, "1/%d Secs", data->slider.value + 1); } } @@ -1049,14 +1033,6 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerResolution, }, - { - MENUITEMTYPE_DROPDOWN, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Texture Filtering", - 0, - menuhandlerTexFilter, - }, { MENUITEMTYPE_CHECKBOX, 0, @@ -1073,6 +1049,22 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerCenterWindow, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"HUD Centering", + 0, + menuhandlerCenterHUD, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, { MENUITEMTYPE_DROPDOWN, 0, @@ -1098,21 +1090,53 @@ struct menuitem g_ExtendedVideoMenuItems[] = { menuhandlerDisplayFPS, }, { - MENUITEMTYPE_DROPDOWN, + MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Display FPS Interval", - 0, + 31, menuhandlerDisplayFPSInterval, }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, { MENUITEMTYPE_DROPDOWN, 0, MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Anti-aliasing (MSAA)", + (uintptr_t)"Anti-aliasing", 0, menuhandlerMSAA, }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Texture Filtering", + 0, + menuhandlerTexFilter, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"GUI Texture Filtering", + 0, + menuhandlerTexFilter2D, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, { MENUITEMTYPE_CHECKBOX, 0, @@ -1121,14 +1145,6 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerTexDetail, }, - { - MENUITEMTYPE_CHECKBOX, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"GUI Texture Filtering", - 0, - menuhandlerTexFilter2D, - }, { MENUITEMTYPE_CHECKBOX, 0, @@ -1137,14 +1153,6 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, menuhandlerGeMuzzleFlashes, }, - { - MENUITEMTYPE_DROPDOWN, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"HUD Centering", - 0, - menuhandlerCenterHUD, - }, { MENUITEMTYPE_SLIDER, 0, diff --git a/port/src/video.c b/port/src/video.c index 9a121421d..7430204e8 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -42,7 +42,7 @@ static s32 vidVsync = 1; static s32 vidMSAA = 1; static s32 vidFramerateLimit = 0; static s32 vidDisplayFPS = 0; -static s32 vidDisplayFPSDivisor = 8; +static s32 vidDisplayFPSDivisor = 16; static displaymode vidModeDefault; static s32 vidNumModes = 1; static displaymode *vidModes = &vidModeDefault; @@ -529,7 +529,7 @@ PD_CONSTRUCTOR static void videoConfigInit(void) configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1); configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, VIDEO_MAX_FPS); configRegisterInt("Video.DisplayFPS", &vidDisplayFPS, 0, 1); - configRegisterInt("Video.DisplayFPSDivisor", &vidDisplayFPSDivisor, 1, 16); + configRegisterInt("Video.DisplayFPSDivisor", &vidDisplayFPSDivisor, 1, 32); configRegisterInt("Video.MSAA", &vidMSAA, 1, 16); configRegisterInt("Video.TextureFilter", &texFilter, 0, 2); configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1); From 687b966c5eb736ae1311d9a6e1bcd97ed160a9dd Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Sat, 30 Nov 2024 20:29:11 -0500 Subject: [PATCH 11/12] Allow overriding g_TickRateDiv at runtime --- port/src/main.c | 2 ++ port/src/optionsmenu.c | 69 +++++++++++++++++++++++++++++++++++++++++- src/include/data.h | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/port/src/main.c b/port/src/main.c index f8f6af13f..c745c0efb 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -36,6 +36,7 @@ u32 g_VmNumPageReplaces = 0; u8 g_VmShowStats = 0; s32 g_TickRateDiv = 1; +s32 g_TickRateDivOverride = 0; s32 g_TickExtraSleep = true; s32 g_SkipIntro = false; @@ -165,6 +166,7 @@ PD_CONSTRUCTOR static void gameConfigInit(void) configRegisterInt("Game.MenuMouseControl", &g_MenuMouseControl, 0, 1); configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f); configRegisterInt("Game.TickRateDivisor", &g_TickRateDiv, 0, 10); + configRegisterInt("Game.TickRateDivisorOverride", &g_TickRateDivOverride, 0, 1); configRegisterInt("Game.ExtraSleep", &g_TickExtraSleep, 0, 1); configRegisterInt("Game.SkipIntro", &g_SkipIntro, 0, 1); configRegisterInt("Game.DisableMpDeathMusic", &g_MusicDisableMpDeath, 0, 1); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index e26c8efcd..1d03bc169 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -775,7 +775,7 @@ static MenuItemHandlerResult menuhandlerFramerateLimit(s32 operation, struct men data->slider.value = videoGetFramerateLimit(); break; case MENUOP_SET: - if (g_TickRateDiv < 2) { + if (!g_TickRateDivOverride) { g_TickRateDiv = (data->slider.value == 0 || data->slider.value > 60) ? 0 : 1; } videoSetFramerateLimit(data->slider.value); @@ -960,6 +960,49 @@ static MenuItemHandlerResult menuhandlerGeMuzzleFlashes(s32 operation, struct me return 0; } +static MenuItemHandlerResult menuhandlerTickrateDivisorOverride(s32 operation, struct menuitem *item, union handlerdata *data) +{ + s32 framerateLimit; + + switch (operation) { + case MENUOP_GET: + return g_TickRateDivOverride; + case MENUOP_SET: + g_TickRateDivOverride = data->checkbox.value; + if (!g_TickRateDivOverride) { + framerateLimit = videoGetFramerateLimit(); + g_TickRateDiv = (framerateLimit == 0 || framerateLimit > 60) ? 0 : 1; + } + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerTickrateDivisor(s32 operation, struct menuitem *item, union handlerdata *data) +{ + s32 framerateLimit; + + switch (operation) { + case MENUOP_CHECKHIDDEN: + if (!g_TickRateDivOverride) { + return true; + } + break; + case MENUOP_GETSLIDER: + data->slider.value = g_TickRateDiv; + break; + case MENUOP_SET: + if (g_TickRateDivOverride) { + g_TickRateDiv = data->slider.value; + } else { + framerateLimit = videoGetFramerateLimit(); + g_TickRateDiv = (framerateLimit == 0 || framerateLimit > 60) ? 0 : 1; + } + } + + return 0; +} + static MenuItemHandlerResult menuhandlerCenterHUD(s32 operation, struct menuitem *item, union handlerdata *data) { static const char *opts[] = { @@ -1169,6 +1212,30 @@ struct menuitem g_ExtendedVideoMenuItems[] = { 0, NULL, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Override Tickrate Divisor", + 0, + menuhandlerTickrateDivisorOverride, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"Tickrate Divisor", + 10, + menuhandlerTickrateDivisor, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, { MENUITEMTYPE_SELECTABLE, 0, diff --git a/src/include/data.h b/src/include/data.h index 24c106292..6a5148ca0 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -550,6 +550,7 @@ extern s32 g_BlurFb; extern s32 g_BlurFbCapTimer; extern bool g_BlurFbDirty; extern s32 g_TickRateDiv; +extern s32 g_TickRateDivOverride; extern s32 g_TickExtraSleep; extern s32 g_MusicDisableMpDeath; extern s32 g_BgunGeMuzzleFlashes; From e2e5bc352d56cbfb4438edf459d8e547b2299f06 Mon Sep 17 00:00:00 2001 From: Matthew Coppola Date: Fri, 6 Dec 2024 16:53:38 -0500 Subject: [PATCH 12/12] Call text_end (text0f153780) for correctness sake --- src/game/lv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/lv.c b/src/game/lv.c index 06bff3b84..bd2a37c2f 100644 --- a/src/game/lv.c +++ b/src/game/lv.c @@ -974,6 +974,7 @@ Gfx *lvRenderFPS(Gfx *gdl) gdl = text0f153628(gdl); gdl = textRender(gdl, &x, &y, buffer, g_CharsNumeric, g_FontNumeric, color, 0x000000a0, viGetWidth(), viGetHeight(), 0, 0); + gdl = text0f153780(gdl); gSPClearExtraGeometryModeEXT(gdl++, g_HudAlignModeL); }