Skip to content

Commit 9a3af38

Browse files
committed
Improved fps cap menu options
1 parent 9fda23b commit 9a3af38

File tree

7 files changed

+98
-28
lines changed

7 files changed

+98
-28
lines changed

config_spec.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ display:
165165
vsync:
166166
type: bool
167167
default: true
168+
fps_cap:
169+
type: enum
170+
values: [none, 15_fps, 30_fps, 60_fps, 120_fps, 144_fps, custom]
171+
default: 60_fps
172+
custom_fps_cap:
173+
type: number
174+
default: 60.0
168175
ui:
169176
show_menubar:
170177
type: bool

ui/xemu.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ static bool alt_grab;
106106
static bool ctrl_grab;
107107
static int gui_saved_grab;
108108
static int gui_fullscreen;
109-
static int fps_selection = 3;
110109
static float frame_deadline = (NANOSECONDS_PER_SECOND / 60);
111110
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
112111
static SDL_Cursor *sdl_cursor_normal;
@@ -134,23 +133,29 @@ void xemu_toggle_fullscreen(void)
134133
toggle_full_screen(&sdl2_console[0]);
135134
}
136135

137-
int xemu_get_frame_rate_cap(void)
138-
{
139-
return fps_selection;
140-
}
141136

142-
void xemu_set_frame_rate_cap(int selection)
137+
void xemu_update_frame_rate_cap(void)
143138
{
144-
// FIXME: Remove redundent selection
145-
fps_selection = selection;
139+
int selection = g_config.display.window.fps_cap;
146140

147141
// No framerate cap
148142
if (!selection) {
149143
frame_deadline = 0;
144+
return;
150145
}
151146

152-
const int framerates[6] = { 60, 15, 30, 60, 120, 144 };
153-
int frame_rate = framerates[selection];
147+
// NOTE: Should we log in someway if the selection escaped its valid range?
148+
// For now we default to custom.
149+
float frame_rate;
150+
const float frame_rates[5] = { 15, 30, 60, 120, 144 };
151+
if (selection < CONFIG_DISPLAY_WINDOW_FPS_CAP_CUSTOM){
152+
frame_rate = frame_rates[selection - 1];
153+
} else {
154+
frame_rate = g_config.display.window.custom_fps_cap;
155+
}
156+
157+
// Calculate the minimum time allowed between frames based on
158+
// the desired frame rate
154159
frame_deadline = (float)NANOSECONDS_PER_SECOND / frame_rate;
155160
}
156161

@@ -1413,6 +1418,9 @@ int main(int argc, char **argv)
14131418
set_full_screen(&sdl2_console[0], gui_fullscreen);
14141419
}
14151420

1421+
// Check settings and update the frame rate cap
1422+
xemu_update_frame_rate_cap();
1423+
14161424
/*
14171425
* FIXME: May want to create a callback mechanism for main QEMU thread
14181426
* to just run functions to avoid TLS bugs and locking issues.

ui/xui/main-menu.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,9 +554,31 @@ void MainMenuDisplayView::Draw()
554554
"3840x2160\0",
555555
"Select preferred startup window size")) {
556556
}
557+
557558
Toggle("Vertical refresh sync", &g_config.display.window.vsync,
558559
"Sync to screen vertical refresh to reduce tearing artifacts");
559560

561+
if (ChevronCombo("Frame Rate Cap", &g_config.display.window.fps_cap,
562+
"None\0"
563+
"15fps\0"
564+
"30fps\0"
565+
"60fps\0"
566+
"120fps\0"
567+
"144fps\0"
568+
"Custom\0",
569+
"Limit the maximum frames per second")) {
570+
xemu_update_frame_rate_cap();
571+
}
572+
573+
char custom_refresh_rate[32];
574+
snprintf(custom_refresh_rate, sizeof(custom_refresh_rate),
575+
"Cap Refresh Rate at %dfps\0", (int)g_config.display.window.custom_fps_cap);
576+
if (g_config.display.window.fps_cap == CONFIG_DISPLAY_WINDOW_FPS_CAP_CUSTOM) {
577+
if(Slider("Custom FPS Limit", &g_config.display.window.custom_fps_cap, 10, 300, custom_refresh_rate)) {
578+
xemu_update_frame_rate_cap();
579+
}
580+
}
581+
560582
SectionTitle("Interface");
561583
Toggle("Show main menu bar", &g_config.display.ui.show_menubar,
562584
"Show main menu bar when mouse is activated");

ui/xui/menubar.cc

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,37 @@ void ShowMainMenu()
194194
nv2a_set_surface_scale_factor(rendering_scale + 1);
195195
}
196196

197-
int max_fps = xemu_get_frame_rate_cap();
198-
if(ImGui::Combo("Frame Rate Cap", &max_fps,
199-
"none\0"
200-
"15fps\0"
201-
"30fps\0"
202-
"60fps\0"
203-
"120fps\0"
204-
"144fps\0")) {
205-
xemu_set_frame_rate_cap(max_fps);
206-
}
197+
198+
// Define the string array used for fps selection in the menu bar
199+
// Split the string into a set and custom region, since we will have
200+
// to format a section of the string, but can't format the whole string
201+
// due to the null terminations.
202+
//
203+
// We need to both allocate a string beginning with the base options
204+
// and track the substring size, so just use a macro.
205+
#ifndef FPS_BASE_OPTIONS_STR
206+
#define FPS_BASE_OPTIONS_STR "none\0""15fps\0""30fps\0""60fps\0""120fps\0""144fps"
207+
#endif
208+
209+
// Allocate the fps options buffer
210+
char fps_options[64] = FPS_BASE_OPTIONS_STR;
211+
// Split into regions
212+
const size_t fps_options_set_region_size = sizeof(FPS_BASE_OPTIONS_STR);
213+
char *fps_options_custom_region = fps_options + fps_options_set_region_size;
214+
size_t fps_options_custom_region_size = sizeof(fps_options) - fps_options_set_region_size;
215+
// Clean up the macro (kinda gross we that we need one, but better than a magic number)
216+
#undef FPS_BASE_OPTIONS_STR
217+
218+
// Format the custom region to specifc the custom FPS.
219+
// Clear the custom region first! If the custom FPS jumps by over a digit and the
220+
// custom region is not cleared, an additional selection option may be created.
221+
memset(fps_options_custom_region, '\0', fps_options_custom_region_size);
222+
snprintf(fps_options_custom_region, fps_options_custom_region_size,
223+
"Custom (%dfps)",(int)g_config.display.window.custom_fps_cap);
224+
225+
if(ImGui::Combo("Frame Rate Cap", &g_config.display.window.fps_cap, fps_options)) {
226+
xemu_update_frame_rate_cap();
227+
}
207228

208229
ImGui::Combo("Display Mode", &g_config.display.ui.fit,
209230
"Center\0Scale\0Stretch\0");

ui/xui/widgets.cc

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,16 @@ bool Toggle(const char *str_id, bool *v, const char *description)
222222
return status;
223223
}
224224

225-
void Slider(const char *str_id, float *v, const char *description)
225+
bool Slider(const char *str_id, float *v, const char *description)
226226
{
227+
return Slider(str_id, v, 0, 1, description);
228+
}
229+
230+
bool Slider(const char *str_id, float *v, float min, float max, const char *description)
231+
{
232+
float initial = *v;
233+
float pos = (initial - min) / (max - min);
234+
227235
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32_BLACK_TRANS);
228236

229237
ImGuiStyle &style = ImGui::GetStyle();
@@ -261,13 +269,13 @@ void Slider(const char *str_id, float *v, const char *description)
261269
ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft) ||
262270
ImGui::IsKeyPressed(ImGuiKey_GamepadLStickLeft) ||
263271
ImGui::IsKeyPressed(ImGuiKey_GamepadRStickLeft)) {
264-
*v -= 0.05;
272+
pos -= 0.05;
265273
}
266274
if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) ||
267275
ImGui::IsKeyPressed(ImGuiKey_GamepadDpadRight) ||
268276
ImGui::IsKeyPressed(ImGuiKey_GamepadLStickRight) ||
269277
ImGui::IsKeyPressed(ImGuiKey_GamepadRStickRight)) {
270-
*v += 0.05;
278+
pos += 0.05;
271279
}
272280

273281
if (
@@ -286,16 +294,19 @@ void Slider(const char *str_id, float *v, const char *description)
286294

287295
if (ImGui::IsItemActive()) {
288296
ImVec2 mouse = ImGui::GetMousePos();
289-
*v = GetSliderValueForMousePos(mouse, slider_pos, slider_size);
297+
pos = GetSliderValueForMousePos(mouse, slider_pos, slider_size);
290298
}
291-
*v = fmax(0, fmin(*v, 1));
292-
DrawSlider(*v, ImGui::IsItemHovered() || ImGui::IsItemActive(), slider_pos,
299+
pos = fmax(0, fmin(pos, 1));
300+
DrawSlider(pos, ImGui::IsItemHovered() || ImGui::IsItemActive(), slider_pos,
293301
slider_size);
294302

295303
ImVec2 slider_max = ImVec2(slider_pos.x + slider_size.x, slider_pos.y + slider_size.y);
296304
ImGui::RenderNavHighlight(ImRect(slider_pos, slider_max), window->GetID("###slider"));
297305

298306
ImGui::PopStyleColor();
307+
308+
*v = (pos * (max - min)) + min;
309+
return *v != initial;
299310
}
300311

301312
bool FilePicker(const char *str_id, const char **buf, const char *filters,

ui/xui/widgets.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ float GetSliderValueForMousePos(ImVec2 mouse, ImVec2 pos, ImVec2 size);
3434
void DrawSlider(float v, bool hovered, ImVec2 pos, ImVec2 size);
3535
void DrawToggle(bool enabled, bool hovered, ImVec2 pos, ImVec2 size);
3636
bool Toggle(const char *str_id, bool *v, const char *description = nullptr);
37-
void Slider(const char *str_id, float *v, const char *description = nullptr);
37+
bool Slider(const char *str_id, float *v, const char *description = nullptr);
38+
bool Slider(const char *str_id, float *v, float min, float max, const char *description = nullptr);
3839
bool FilePicker(const char *str_id, const char **buf, const char *filters,
3940
bool dir = false);
4041
void DrawComboChevron();

ui/xui/xemu-hud.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ extern "C" {
3434
int xemu_is_fullscreen(void);
3535
void xemu_toggle_fullscreen(void);
3636
int xemu_get_frame_rate_cap(void);
37-
void xemu_set_frame_rate_cap(int selection);
37+
void xemu_update_frame_rate_cap(void);
3838
void xemu_eject_disc(Error **errp);
3939
void xemu_load_disc(const char *path, Error **errp);
4040

0 commit comments

Comments
 (0)