Add experimental framelimiter mode (TAISEI_FRAMELIMITER_LOGIC_ONLY)
Also fixed a whole bunch of state being updated in menu draw code
This commit is contained in:
parent
bc0d6795a3
commit
97d3e80f6f
8 changed files with 117 additions and 27 deletions
|
@ -56,6 +56,8 @@ In addition to the variables listed here, those processed by our runtime depende
|
|||
|
||||
* **TAISEI_FRAMELIMITER_COMPENSATE** *(default value: `1`)*: if `1`, the framerate limiter may let frames finish earlier than normal after sudden frametime spikes. This achieves better timing accuracy, but may hurt fluidity if the framerate is too unstable.
|
||||
|
||||
* **TAISEI_FRAMELIMITER_LOGIC_ONLY** *(default value: `0`)*: **EXPERIMENTAL**: if `1`, only the logic framerate will be capped; new rendering frames will be processed as quickly as possible, with no delay. This inherently desynchronizes logic and rendering frames, and therefore, some logic frames may be dropped if rendering is too slow. However, unlike with the synchronous mode, the game speed will remain roughly constant in those cases. `TAISEI_FRAMELIMITER_SLEEP`, `TAISEI_FRAMELIMITER_COMPENSATE`, and the `frameskip` setting have no effect in this mode.
|
||||
|
||||
### Logging
|
||||
|
||||
Taisei's logging system currently has four basic levels and works by dispatching messages to a few output handlers. Each handler has a level filter, which is configured by a separate environment variable. All of those variables work the same way: their value looks like an IRC mode string, and represents a modification of the handler's default settings. If this doesn't make sense, take a look at the *Examples* section.
|
||||
|
|
|
@ -136,10 +136,8 @@ void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int)
|
|||
|
||||
draw_menu_selector(m->drawdata[0], m->drawdata[2], m->drawdata[1], 34, m->frames);
|
||||
|
||||
int i;
|
||||
for(i = 0; i < m->ecount; i++) {
|
||||
for(int i = 0; i < m->ecount; ++i) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
e->drawdata += 0.2 * (10*(i == m->cursor) - e->drawdata);
|
||||
|
||||
float p = offset + 20*i;
|
||||
|
||||
|
@ -164,6 +162,17 @@ void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int)
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
void animate_menu_list_entry(MenuData *m, int i) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
e->drawdata += 0.2 * (10*(i == m->cursor) - e->drawdata);
|
||||
}
|
||||
|
||||
void animate_menu_list_entries(MenuData *m) {
|
||||
for(int i = 0; i < m->ecount; ++i) {
|
||||
animate_menu_list_entry(m, i);
|
||||
}
|
||||
}
|
||||
|
||||
void animate_menu_list(MenuData *m) {
|
||||
MenuEntry *s = m->entries + m->cursor;
|
||||
int w = stringwidth(s->name, _fonts.standard);
|
||||
|
@ -171,6 +180,8 @@ void animate_menu_list(MenuData *m) {
|
|||
m->drawdata[0] += (10 + w/2.0 - m->drawdata[0])/10.0;
|
||||
m->drawdata[1] += (w*2 - m->drawdata[1])/10.0;
|
||||
m->drawdata[2] += (20*m->cursor - m->drawdata[2])/10.0;
|
||||
|
||||
animate_menu_list_entries(m);
|
||||
}
|
||||
|
||||
void menu_commonaction_close(MenuData *menu, void *arg) {
|
||||
|
|
|
@ -17,4 +17,6 @@ void draw_menu_selector(float x, float y, float w, float h, float t);
|
|||
void draw_menu_title(MenuData *m, char *title);
|
||||
void draw_menu_list(MenuData *m, float x, float y, void (*draw)(void*, int, int));
|
||||
void animate_menu_list(MenuData *m);
|
||||
void animate_menu_list_entries(MenuData *m);
|
||||
void animate_menu_list_entry(MenuData *m, int i);
|
||||
void menu_commonaction_close(MenuData *menu, void *arg);
|
||||
|
|
|
@ -14,12 +14,21 @@
|
|||
#include "common.h"
|
||||
#include "global.h"
|
||||
|
||||
// FIXME: put this into the menu struct somehow (drawdata is a bad system)
|
||||
static Color diff_color;
|
||||
|
||||
void set_difficulty(MenuData *m, void *d) {
|
||||
progress.game_settings.difficulty = (Difficulty)(uintptr_t)d;
|
||||
}
|
||||
|
||||
void update_difficulty_menu(MenuData *menu) {
|
||||
menu->drawdata[0] += (menu->cursor-menu->drawdata[0])*0.1;
|
||||
|
||||
for(int i = 0; i < menu->ecount; ++i) {
|
||||
menu->entries[i].drawdata += 0.2 * (30*(i == menu->cursor) - menu->entries[i].drawdata);
|
||||
}
|
||||
|
||||
diff_color = approach_color(diff_color, difficulty_color(menu->cursor + D_Easy), 0.1);
|
||||
}
|
||||
|
||||
void create_difficulty_menu(MenuData *m) {
|
||||
|
@ -48,10 +57,7 @@ void draw_difficulty_menu(MenuData *menu) {
|
|||
draw_options_menu_bg(menu);
|
||||
draw_menu_title(menu, "Select Difficulty");
|
||||
|
||||
static Color clr = 0;
|
||||
|
||||
clr = approach_color(clr, difficulty_color(menu->cursor + D_Easy), 0.1);
|
||||
parse_color_call(multiply_colors(clr, rgba(0.1, 0.1, 0.1, 0.7)), glColor4f);
|
||||
parse_color_call(multiply_colors(diff_color, rgba(0.1, 0.1, 0.1, 0.7)), glColor4f);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(SCREEN_W/2+30 - 25*menu->drawdata[0], SCREEN_H/3 + 90*(0.7*menu->drawdata[0]),0);
|
||||
|
@ -67,10 +73,7 @@ void draw_difficulty_menu(MenuData *menu) {
|
|||
|
||||
glPopMatrix();
|
||||
|
||||
int i;
|
||||
for(i = 0; i < menu->ecount; i++) {
|
||||
menu->entries[i].drawdata += 0.2 * (30*(i == menu->cursor) - menu->entries[i].drawdata);
|
||||
|
||||
for(int i = 0; i < menu->ecount; ++i) {
|
||||
glPushMatrix();
|
||||
glTranslatef(SCREEN_W/2 + SCREEN_W*sign((i&1)-0.5)*(i!=menu->cursor)*menu_fade(menu) - (int)menu->entries[i].drawdata+25*i-50, SCREEN_H/3 + 90*(i-0.3*menu->drawdata[0]),0);
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ static void replayview_draw_stagemenu(MenuData *m) {
|
|||
|
||||
for(i = 0; i < m->ecount; ++i) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
float a = e->drawdata += 0.2 * ((i == m->cursor) - e->drawdata);
|
||||
float a = e->drawdata;
|
||||
|
||||
if(e->action == NULL) {
|
||||
glColor4f(0.5, 0.5, 0.5, 0.5 * alpha);
|
||||
|
@ -208,6 +208,11 @@ static void replayview_logic(MenuData *m) {
|
|||
if(ctx->submenu) {
|
||||
MenuData *sm = ctx->submenu;
|
||||
|
||||
for(int i = 0; i < sm->ecount; ++i) {
|
||||
MenuEntry *e = sm->entries + i;
|
||||
e->drawdata += 0.2 * ((i == m->cursor) - e->drawdata);
|
||||
}
|
||||
|
||||
if(sm->state == MS_Dead) {
|
||||
if(ctx->sub_fade == 1.0) {
|
||||
destroy_menu(sm);
|
||||
|
@ -225,6 +230,8 @@ static void replayview_logic(MenuData *m) {
|
|||
m->drawdata[0] = SCREEN_W/2 - 100;
|
||||
m->drawdata[1] = (SCREEN_W - 200)*1.75;
|
||||
m->drawdata[2] += (20*m->cursor - m->drawdata[2])/10.0;
|
||||
|
||||
animate_menu_list_entries(m);
|
||||
}
|
||||
|
||||
static void replayview_draw(MenuData *m) {
|
||||
|
|
|
@ -44,8 +44,6 @@ static void save_rpy(MenuData *menu, void *a) {
|
|||
}
|
||||
|
||||
static void draw_saverpy_menu(MenuData *m) {
|
||||
int i;
|
||||
|
||||
draw_options_menu_bg(m);
|
||||
|
||||
draw_menu_selector(SCREEN_W/2 + 100 * m->drawdata[0] - 50, SCREEN_H/2, 163, 81, m->frames);
|
||||
|
@ -56,17 +54,13 @@ static void draw_saverpy_menu(MenuData *m) {
|
|||
draw_text(AL_Center, 0, 0, "Save Replay?", _fonts.mainmenu);
|
||||
glTranslatef(0, 100, 0);
|
||||
|
||||
m->drawdata[0] += (m->cursor - m->drawdata[0])/10.0;
|
||||
|
||||
for(i = 0; i < m->ecount; i++) {
|
||||
for(int i = 0; i < m->ecount; i++) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
|
||||
e->drawdata += 0.2 * (10*(i == m->cursor) - e->drawdata);
|
||||
float a = e->drawdata * 0.1;
|
||||
|
||||
if(e->action == NULL)
|
||||
if(e->action == NULL) {
|
||||
glColor4f(0.5, 0.5, 0.5, 0.5);
|
||||
else {
|
||||
} else {
|
||||
float ia = 1-a;
|
||||
glColor4f(0.9 + ia * 0.1, 0.6 + ia * 0.4, 0.2 + ia * 0.8, 0.7 + 0.3 * a);
|
||||
}
|
||||
|
@ -96,11 +90,20 @@ static void saverpy_menu_input(MenuData *menu) {
|
|||
}, EFLAG_MENU);
|
||||
}
|
||||
|
||||
static void update_saverpy_menu(MenuData *m) {
|
||||
m->drawdata[0] += (m->cursor - m->drawdata[0])/10.0;
|
||||
|
||||
for(int i = 0; i < m->ecount; i++) {
|
||||
MenuEntry *e = &(m->entries[i]);
|
||||
e->drawdata += 0.2 * (10*(i == m->cursor) - e->drawdata);
|
||||
}
|
||||
}
|
||||
|
||||
void create_saverpy_menu(MenuData *m) {
|
||||
create_menu(m);
|
||||
m->input = saverpy_menu_input;
|
||||
m->draw = draw_saverpy_menu;
|
||||
|
||||
m->logic = update_saverpy_menu;
|
||||
m->flags = MF_Transient;
|
||||
|
||||
add_menu_entry(m, "Yes", save_rpy, NULL);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
void draw_spell_menu(MenuData *m) {
|
||||
draw_options_menu_bg(m);
|
||||
draw_menu_title(m, "Spell Practice");
|
||||
animate_menu_list(m);
|
||||
draw_menu_list(m, 100, 100, NULL);
|
||||
}
|
||||
|
||||
|
@ -26,6 +25,7 @@ void create_spell_menu(MenuData *m) {
|
|||
|
||||
create_menu(m);
|
||||
m->draw = draw_spell_menu;
|
||||
m->logic = animate_menu_list;
|
||||
m->flags = MF_Abortable;
|
||||
m->transition = TransMenuDark;
|
||||
|
||||
|
|
70
src/util.c
70
src/util.c
|
@ -365,14 +365,29 @@ void loop_at_fps(LogicFrameFunc logic_frame, RenderFrameFunc render_frame, void
|
|||
int32_t delay = getenvint("TAISEI_FRAMELIMITER_SLEEP", 0);
|
||||
bool exact_delay = getenvint("TAISEI_FRAMELIMITER_SLEEP_EXACT", 1);
|
||||
bool compensate = getenvint("TAISEI_FRAMELIMITER_COMPENSATE", 1);
|
||||
bool uncapped_rendering_env = getenvint("TAISEI_FRAMELIMITER_LOGIC_ONLY", 0);
|
||||
bool late_swap = config_get_int(CONFIG_VID_LATE_SWAP);
|
||||
|
||||
uint32_t frame_num = 0;
|
||||
|
||||
// don't care about thread safety, we can render only on the main thread anyway
|
||||
static uint8_t recursion_detector;
|
||||
++recursion_detector;
|
||||
|
||||
while(true) {
|
||||
bool uncapped_rendering = uncapped_rendering_env;
|
||||
frame_start_time = time_get();
|
||||
|
||||
begin_frame:
|
||||
|
||||
#ifdef DEBUG
|
||||
if(gamekeypressed(KEY_FPSLIMIT_OFF)) {
|
||||
uncapped_rendering = false;
|
||||
} else {
|
||||
uncapped_rendering = uncapped_rendering_env;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(late_swap && rframe_action == RFRAME_SWAP) {
|
||||
video_swap_buffers();
|
||||
}
|
||||
|
@ -380,9 +395,57 @@ begin_frame:
|
|||
global.fps.busy.last_update_time = time_get();
|
||||
|
||||
++frame_num;
|
||||
lframe_action = logic_frame(arg);
|
||||
|
||||
if(frame_num % get_effective_frameskip()) {
|
||||
if(uncapped_rendering) {
|
||||
uint32_t logic_frames = 0;
|
||||
|
||||
while(lframe_action != LFRAME_STOP && next_frame_time < frame_start_time) {
|
||||
uint8_t rval = recursion_detector;
|
||||
|
||||
lframe_action = logic_frame(arg);
|
||||
fpscounter_update(&global.fps.logic);
|
||||
++logic_frames;
|
||||
|
||||
if(rval != recursion_detector) {
|
||||
log_debug(
|
||||
"Recursive call detected (%u != %u), resetting next frame time to avoid a large skip",
|
||||
rval,
|
||||
recursion_detector
|
||||
);
|
||||
next_frame_time = time_get() + target_frame_time;
|
||||
break;
|
||||
}
|
||||
|
||||
hrtime_t frametime = target_frame_time;
|
||||
|
||||
if(lframe_action == LFRAME_SKIP) {
|
||||
frametime *= 0.1;
|
||||
}
|
||||
|
||||
next_frame_time += frametime;
|
||||
|
||||
hrtime_t total = time_get() - frame_start_time;
|
||||
|
||||
if(total > target_frame_time) {
|
||||
next_frame_time = frame_start_time;
|
||||
log_debug("Executing logic took too long (%f), giving up", (double)total);
|
||||
}
|
||||
}
|
||||
|
||||
if(logic_frames > 1) {
|
||||
log_debug(
|
||||
"Dropped %u logic frame%s in superframe #%u",
|
||||
logic_frames - 1,
|
||||
logic_frames > 2 ? "s" : "",
|
||||
frame_num
|
||||
);
|
||||
}
|
||||
} else {
|
||||
lframe_action = logic_frame(arg);
|
||||
fpscounter_update(&global.fps.logic);
|
||||
}
|
||||
|
||||
if(!uncapped_rendering && frame_num % get_effective_frameskip()) {
|
||||
rframe_action = RFRAME_DROP;
|
||||
} else {
|
||||
rframe_action = render_frame(arg);
|
||||
|
@ -397,10 +460,9 @@ begin_frame:
|
|||
video_swap_buffers();
|
||||
}
|
||||
|
||||
fpscounter_update(&global.fps.logic);
|
||||
fpscounter_update(&global.fps.busy);
|
||||
|
||||
if(lframe_action == LFRAME_SKIP) {
|
||||
if(lframe_action == LFRAME_SKIP || uncapped_rendering) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue