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:
Andrei Alexeyev 2018-01-18 18:40:42 +02:00
parent bc0d6795a3
commit 97d3e80f6f
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
8 changed files with 117 additions and 27 deletions

View file

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

View file

@ -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) {

View file

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

View file

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

View file

@ -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) {

View file

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

View file

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

View file

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