frameskip option for slow GPUs

This commit is contained in:
Andrei Alexeyev 2017-12-26 13:07:40 +02:00
parent d66793a117
commit 48b13cd83a
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
21 changed files with 246 additions and 122 deletions

View file

@ -78,6 +78,7 @@
CONFIGDEF_INT (VID_HEIGHT, "vid_height", RESY) \
CONFIGDEF_INT (VID_RESIZABLE, "vid_resizable", 0) \
CONFIGDEF_INT (VID_LATE_SWAP, "vid_late_swap", 0) \
CONFIGDEF_INT (VID_FRAMESKIP, "vid_frameskip", 1) \
CONFIGDEF_INT (VSYNC, "vsync", 0) \
CONFIGDEF_INT (MIXER_CHUNKSIZE, "mixer_chunksize", 1024) \
CONFIGDEF_FLOAT (SFX_VOLUME, "sfx_volume", 1.0) \

View file

@ -262,18 +262,22 @@ void credits_preload(void) {
NULL);
}
static FrameAction credits_frame(void *arg) {
static FrameAction credits_logic_frame(void *arg) {
update_transition();
events_poll(NULL, 0);
credits_process();
credits_draw();
global.frames++;
return credits.end == 0 ? FRAME_STOP : FRAME_SWAP;
return credits.end == 0 ? LFRAME_STOP : LFRAME_WAIT;
}
static FrameAction credits_render_frame(void *arg) {
credits_draw();
return RFRAME_SWAP;
}
void credits_loop(void) {
credits_preload();
credits_init();
loop_at_fps(credits_frame, NULL, NULL, FPS);
loop_at_fps(credits_logic_frame, credits_render_frame, NULL, FPS);
credits_free();
}

View file

@ -151,17 +151,16 @@ void ending_preload(void) {
preload_resource(RES_BGM, "ending", RESF_OPTIONAL);
}
static FrameAction ending_frame(void *arg) {
static FrameAction ending_logic_frame(void *arg) {
Ending *e = arg;
update_transition();
events_poll(NULL, 0);
if(e->pos >= e->count - 1) {
return FRAME_STOP;
return LFRAME_STOP;
}
ending_draw(e);
global.frames++;
if(global.frames >= e->entries[e->pos+1].time) {
@ -173,7 +172,13 @@ static FrameAction ending_frame(void *arg) {
set_transition(TransFadeWhite, ENDING_FADE_OUT, ENDING_FADE_OUT);
}
return FRAME_SWAP;
return LFRAME_WAIT;
}
static FrameAction ending_render_frame(void *arg) {
Ending *e = arg;
ending_draw(e);
return RFRAME_SWAP;
}
void ending_loop(void) {
@ -183,6 +188,6 @@ void ending_loop(void) {
global.frames = 0;
set_ortho();
start_bgm("ending");
loop_at_fps(ending_frame, NULL, &e, FPS);
loop_at_fps(ending_logic_frame, ending_render_frame, &e, FPS);
free_ending(&e);
}

View file

@ -28,6 +28,10 @@ void init_global(CLIAction *cli) {
if(global.frameskip) {
log_warn("FPS limiter disabled. Gotta go fast! (frameskip = %i)", global.frameskip);
}
fpscounter_reset(&global.fps.logic);
fpscounter_reset(&global.fps.render);
fpscounter_reset(&global.fps.busy);
}
// Inputdevice-agnostic method of checking whether a game control is pressed.

View file

@ -107,8 +107,11 @@ typedef struct {
int game_over;
FPSCounter fps;
FPSCounter fps_busy;
struct {
FPSCounter logic;
FPSCounter render;
FPSCounter busy;
} fps;
Replay replay;
ReplayMode replaymode;

View file

@ -38,10 +38,17 @@ void char_menu_input(MenuData*);
void draw_char_menu(MenuData*);
void free_char_menu(MenuData*);
void update_char_menu(MenuData *menu) {
for(int i = 0; i < menu->ecount; i++) {
menu->entries[i].drawdata += 0.08*(1.0*(menu->cursor != i) - menu->entries[i].drawdata);
}
}
void create_char_menu(MenuData *m) {
create_menu(m);
m->input = char_menu_input;
m->draw = draw_char_menu;
m->logic = update_char_menu;
m->end = free_char_menu;
m->transition = TransMenuDark;
m->flags = MF_Abortable | MF_Transient;
@ -86,8 +93,6 @@ void draw_char_menu(MenuData *menu) {
current_char = pchar->id;
}
menu->entries[i].drawdata += 0.08*(1.0*(menu->cursor != i) - menu->entries[i].drawdata);
glColor4f(1,1,1,1-menu->entries[i].drawdata*2);
draw_texture(SCREEN_W/3-200*menu->entries[i].drawdata, 2*SCREEN_H/3, tex);
@ -195,7 +200,7 @@ bool char_menu_input_handler(SDL_Event *event, void *arg) {
void char_menu_input(MenuData *menu) {
events_poll((EventHandler[]){
{ .proc = char_menu_input_handler, .arg = menu },
{NULL}
{ NULL }
}, EFLAG_MENU);
}

View file

@ -18,9 +18,14 @@ 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;
}
void create_difficulty_menu(MenuData *m) {
create_menu(m);
m->draw = draw_difficulty_menu;
m->logic = update_difficulty_menu;
m->transition = TransMenuDark;
m->flags = MF_Transient | MF_Abortable;
@ -45,7 +50,6 @@ void draw_difficulty_menu(MenuData *menu) {
static Color clr = 0;
menu->drawdata[0] += (menu->cursor-menu->drawdata[0])*0.1;
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);

View file

@ -26,7 +26,7 @@ static void give_up(MenuData *m, void *arg) {
void create_gameover_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->logic = update_ingame_menu;
m->flags = MF_Transient | MF_AlwaysProcessInput;
m->transition = TransEmpty;

View file

@ -30,6 +30,7 @@ void restart_game(MenuData *m, void *arg) {
void create_ingame_menu(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->logic = update_ingame_menu;
m->flags = MF_Abortable | MF_AlwaysProcessInput;
m->transition = TransEmpty;
m->cursor = 1;
@ -49,6 +50,7 @@ static void skip_stage(MenuData *m, void *arg) {
void create_ingame_menu_replay(MenuData *m) {
create_menu(m);
m->draw = draw_ingame_menu;
m->logic = update_ingame_menu;
m->flags = MF_Abortable | MF_AlwaysProcessInput;
m->transition = TransEmpty;
m->cursor = 1;
@ -77,6 +79,11 @@ void draw_ingame_menu_bg(MenuData *menu, float f) {
glUseProgram(0);
}
void update_ingame_menu(MenuData *menu) {
menu->drawdata[0] += (menu->cursor*35 - menu->drawdata[0])/7.0;
menu->drawdata[1] += (stringwidth(menu->entries[menu->cursor].name, _fonts.standard) - menu->drawdata[1])/10.0;
}
void draw_ingame_menu(MenuData *menu) {
glPushMatrix();
@ -88,9 +95,6 @@ void draw_ingame_menu(MenuData *menu) {
draw_menu_selector(0, menu->drawdata[0], menu->drawdata[1]*2, 41, menu->frames);
menu->drawdata[0] += (menu->cursor*35 - menu->drawdata[0])/7.0;
menu->drawdata[1] += (stringwidth(menu->entries[menu->cursor].name, _fonts.standard) - menu->drawdata[1])/10.0;
if(menu->context) {
float s = 0.3 + 0.2 * sin(menu->frames/10.0);
glColor4f(1-s/2, 1-s/2, 1-s, 1-menu_fade(menu));

View file

@ -17,4 +17,6 @@ void draw_ingame_menu(MenuData *menu);
void create_ingame_menu(MenuData *menu);
void create_ingame_menu_replay(MenuData *m);
void update_ingame_menu(MenuData *menu);
void restart_game(MenuData *m, void *arg);

View file

@ -66,13 +66,16 @@ void main_menu_update_practice_menus(void) {
}
}
void begin_main_menu(MenuData *m) {
static void begin_main_menu(MenuData *m) {
start_bgm("menu");
}
static void update_main_menu(MenuData *menu);
void create_main_menu(MenuData *m) {
create_menu(m);
m->draw = draw_main_menu;
m->logic = update_main_menu;
m->begin = begin_main_menu;
add_menu_entry(m, "Start Story", start_game, NULL);
@ -101,6 +104,15 @@ void draw_main_menu_bg(MenuData* menu) {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
static void update_main_menu(MenuData *menu) {
menu->drawdata[1] += (stringwidth(menu->entries[menu->cursor].name, _fonts.mainmenu) - menu->drawdata[1])/10.0;
menu->drawdata[2] += (35*menu->cursor - menu->drawdata[2])/10.0;
for(int i = 0; i < menu->ecount; i++) {
menu->entries[i].drawdata += 0.2 * ((i == menu->cursor) - menu->entries[i].drawdata);
}
}
void draw_main_menu(MenuData *menu) {
draw_main_menu_bg(menu);
@ -118,14 +130,8 @@ void draw_main_menu(MenuData *menu) {
draw_texture_p(0,0,bg);
glPopMatrix();
int i;
menu->drawdata[1] += (stringwidth(menu->entries[menu->cursor].name, _fonts.mainmenu) - menu->drawdata[1])/10.0;
menu->drawdata[2] += (35*menu->cursor - menu->drawdata[2])/10.0;
for(i = 0; i < menu->ecount; i++) {
for(int i = 0; i < menu->ecount; i++) {
float s = 5*sin(menu->frames/80.0 + 20*i);
menu->entries[i].drawdata += 0.2 * ((i == menu->cursor) - menu->entries[i].drawdata);
if(menu->entries[i].action == NULL) {
glColor4f(0.2,0.3,0.5,0.7);

View file

@ -46,7 +46,6 @@ void create_menu(MenuData *menu) {
menu->transition_in_time = FADE_TIME;
menu->transition_out_time = FADE_TIME;
menu->fade = 1.0;
menu->logic = menu_logic;
menu->input = menu_input;
}
@ -134,7 +133,7 @@ bool menu_input_handler(SDL_Event *event, void *arg) {
void menu_input(MenuData *menu) {
events_poll((EventHandler[]){
{ .proc = menu_input_handler, .arg = menu },
{NULL}
{ NULL }
}, EFLAG_MENU);
}
@ -142,14 +141,14 @@ void menu_no_input(MenuData *menu) {
events_poll(NULL, 0);
}
void menu_logic(MenuData *menu) {
menu->frames++;
}
static FrameAction menu_frame(void *arg) {
static FrameAction menu_logic_frame(void *arg) {
MenuData *menu = arg;
menu->logic(menu);
if(menu->logic) {
menu->logic(menu);
}
menu->frames++;
if(menu->state != MS_FadeOut || menu->flags & MF_AlwaysProcessInput) {
assert(menu->input);
@ -159,11 +158,16 @@ static FrameAction menu_frame(void *arg) {
}
update_transition();
return menu->state == MS_Dead ? LFRAME_STOP : LFRAME_WAIT;
}
static FrameAction menu_render_frame(void *arg) {
MenuData *menu = arg;
assert(menu->draw);
menu->draw(menu);
draw_transition();
return menu->state == MS_Dead ? FRAME_STOP : FRAME_SWAP;
return RFRAME_SWAP;
}
int menu_loop(MenuData *menu) {
@ -173,8 +177,7 @@ int menu_loop(MenuData *menu) {
menu->begin(menu);
}
assert(menu->logic != NULL);
loop_at_fps(menu_frame, NULL, menu, FPS);
loop_at_fps(menu_logic_frame, menu_render_frame, menu, FPS);
if(menu->end) {
menu->end(menu);

View file

@ -82,7 +82,6 @@ void add_menu_separator(MenuData *menu);
void create_menu(MenuData *menu);
void destroy_menu(MenuData *menu);
void menu_logic(MenuData *menu);
void menu_input(MenuData *menu);
void menu_no_input(MenuData *menu);

View file

@ -285,6 +285,15 @@ int bind_common_onoffplus_set(OptionBinding *b, int v) {
#define bind_common_int_get bind_common_onoff_inverted_get
#define bind_common_int_set bind_common_onoff_inverted_set
int bind_common_intplus1_get(OptionBinding *b) {
return config_get_int(b->configentry) - 1;
}
int bind_common_intplus1_set(OptionBinding *b, int v) {
return config_set_int(b->configentry, v + 1) - 1;
}
// --- Binding callbacks for individual options --- //
bool bind_audio_dependence(void) {
@ -344,7 +353,7 @@ void destroy_options_menu(MenuData *m) {
}
static void do_nothing(MenuData *menu, void *arg) { }
static void update_options_menu(MenuData *menu);
void options_menu_input(MenuData*);
void create_options_menu_basic(MenuData *m, char *s) {
@ -354,6 +363,7 @@ void create_options_menu_basic(MenuData *m, char *s) {
m->context = s;
m->input = options_menu_input;
m->draw = draw_options_menu;
m->logic = update_options_menu;
m->end = destroy_options_menu;
}
@ -370,19 +380,6 @@ void options_sub_video(MenuData *parent, void *arg) {
b = bind_option(CONFIG_FULLSCREEN, bind_common_onoff_get, bind_common_onoff_set)
); bind_onoff(b);
add_menu_entry(m, "Vertical synchronization", do_nothing,
b = bind_option(CONFIG_VSYNC, bind_common_onoffplus_get, bind_common_onoffplus_set)
); bind_addvalue(b, "on");
bind_addvalue(b, "off");
bind_addvalue(b, "adaptive");
#ifdef DEBUG
add_menu_entry(m, "Swap buffers", do_nothing,
b = bind_option(CONFIG_VID_LATE_SWAP, bind_common_onoff_get, bind_common_onoff_set)
); bind_addvalue(b, "late");
bind_addvalue(b, "early");
#endif
add_menu_entry(m, "Window size", do_nothing,
b = bind_resolution()
); b->setter = bind_resolution_set;
@ -399,6 +396,27 @@ void options_sub_video(MenuData *parent, void *arg) {
add_menu_separator(m);
add_menu_entry(m, "Vertical synchronization", do_nothing,
b = bind_option(CONFIG_VSYNC, bind_common_onoffplus_get, bind_common_onoffplus_set)
); bind_addvalue(b, "on");
bind_addvalue(b, "off");
bind_addvalue(b, "adaptive");
#ifdef DEBUG
add_menu_entry(m, "Swap buffers", do_nothing,
b = bind_option(CONFIG_VID_LATE_SWAP, bind_common_onoff_get, bind_common_onoff_set)
); bind_addvalue(b, "late");
bind_addvalue(b, "early");
#endif
add_menu_entry(m, "Skip frames", do_nothing,
b = bind_option(CONFIG_VID_FRAMESKIP, bind_common_intplus1_get, bind_common_intplus1_set)
); bind_addvalue(b, "0");
bind_addvalue(b, "½");
bind_addvalue(b, "");
add_menu_separator(m);
add_menu_entry(m, "Stage background", do_nothing,
b = bind_option(CONFIG_NO_STAGEBG, bind_common_int_get, bind_common_int_set)
); bind_onoff(b);
@ -687,6 +705,20 @@ void draw_options_menu_bg(MenuData* menu) {
glColor4f(1, 1, 1, 1);
}
static void update_options_menu(MenuData *menu) {
menu->drawdata[0] += ((SCREEN_W/2 - 100) - menu->drawdata[0])/10.0;
menu->drawdata[1] += ((SCREEN_W - 200) * 1.75 - menu->drawdata[1])/10.0;
menu->drawdata[2] += (20*menu->cursor - menu->drawdata[2])/10.0;
for(int i = 0; i < menu->ecount; i++) {
MenuEntry *e = menu->entries + i;
if(e->name) {
e->drawdata += 0.2 * (10*(i == menu->cursor) - e->drawdata);
}
}
}
void draw_options_menu(MenuData *menu) {
draw_options_menu_bg(menu);
draw_menu_title(menu, menu->context);
@ -696,10 +728,6 @@ void draw_options_menu(MenuData *menu) {
draw_menu_selector(menu->drawdata[0], menu->drawdata[2], menu->drawdata[1], 34, menu->frames);
menu->drawdata[0] += ((SCREEN_W/2 - 100) - menu->drawdata[0])/10.0;
menu->drawdata[1] += ((SCREEN_W - 200) * 1.75 - menu->drawdata[1])/10.0;
menu->drawdata[2] += (20*menu->cursor - menu->drawdata[2])/10.0;
int i, caption_drawn = 2;
for(i = 0; i < menu->ecount; i++) {
@ -709,7 +737,6 @@ void draw_options_menu(MenuData *menu) {
if(!e->name)
continue;
e->drawdata += 0.2 * (10*(i == menu->cursor) - e->drawdata);
float a = e->drawdata * 0.1;
float alpha = (!bind || bind_isactive(bind))? 1 : 0.5;

View file

@ -220,6 +220,10 @@ static void replayview_logic(MenuData *m) {
ctx->sub_fade = approach(ctx->sub_fade, 0.0, 1.0/FADE_TIME);
}
}
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;
}
static void replayview_draw(MenuData *m) {
@ -228,10 +232,6 @@ static void replayview_draw(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Replays");
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;
draw_menu_list(m, 100, 100, replayview_drawitem);
if(ctx->submenu) {

View file

@ -16,7 +16,6 @@
void draw_stgpract_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Stage Practice");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
@ -25,6 +24,7 @@ void create_stgpract_menu(MenuData *m, Difficulty diff) {
create_menu(m);
m->draw = draw_stgpract_menu;
m->logic = animate_menu_list;
m->flags = MF_Abortable;
m->transition = TransMenuDark;

View file

@ -18,7 +18,6 @@
void draw_stage_menu(MenuData *m) {
draw_options_menu_bg(m);
draw_menu_title(m, "Select Stage");
animate_menu_list(m);
draw_menu_list(m, 100, 100, NULL);
}
@ -28,6 +27,7 @@ void create_stage_menu(MenuData *m) {
create_menu(m);
m->draw = draw_stage_menu;
m->logic = animate_menu_list;
m->flags = MF_Abortable;
m->transition = TransMenuDark;

View file

@ -540,13 +540,9 @@ typedef struct StageFrameState {
uint16_t last_replay_fps;
} StageFrameState;
static bool stage_fpslimit_condition(void *arg) {
return (global.replaymode != REPLAY_PLAY || !gamekeypressed(KEY_SKIP)) && !global.frameskip;
}
static void stage_update_fps(StageFrameState *fstate) {
if(global.replaymode == REPLAY_RECORD) {
uint16_t replay_fps = (uint16_t)rint(global.fps.fps);
uint16_t replay_fps = (uint16_t)rint(global.fps.logic.fps);
if(replay_fps != fstate->last_replay_fps) {
replay_stage_event(global.replay_stage, global.frames, EV_FPS, replay_fps);
@ -555,7 +551,7 @@ static void stage_update_fps(StageFrameState *fstate) {
}
}
static FrameAction stage_frame(void *arg) {
static FrameAction stage_logic_frame(void *arg) {
StageFrameState *fstate = arg;
StageInfo *stage = fstate->stage;
@ -586,8 +582,23 @@ static FrameAction stage_frame(void *arg) {
progress.hiscore = global.plr.points;
}
if(global.game_over > 0) {
return LFRAME_STOP;
}
if(global.frameskip || (global.replaymode == REPLAY_PLAY && gamekeypressed(KEY_SKIP))) {
return LFRAME_SKIP;
}
return LFRAME_WAIT;
}
static FrameAction stage_render_frame(void *arg) {
StageFrameState *fstate = arg;
StageInfo *stage = fstate->stage;
if(global.frameskip && global.frames % global.frameskip) {
return FRAME_DROP;
return RFRAME_DROP;
}
tsrand_lock(&global.rand_game);
@ -597,10 +608,9 @@ static FrameAction stage_frame(void *arg) {
END_DRAW_CODE();
tsrand_unlock(&global.rand_game);
tsrand_switch(&global.rand_game);
draw_transition();
return (global.game_over > 0) ? FRAME_STOP : FRAME_SWAP;
return RFRAME_SWAP;
}
void stage_loop(StageInfo *stage) {
@ -676,8 +686,7 @@ void stage_loop(StageInfo *stage) {
display_stage_title(stage);
StageFrameState fstate = { .stage = stage };
fpscounter_reset(&global.fps);
loop_at_fps(stage_frame, stage_fpslimit_condition, &fstate, FPS);
loop_at_fps(stage_logic_frame, stage_render_frame, &fstate, FPS);
if(global.replaymode == REPLAY_RECORD) {
replay_stage_event(global.replay_stage, global.frames, EV_OVER, 0);

View file

@ -573,10 +573,24 @@ void stage_draw_hud_text(struct labels_s* labels) {
// Warning: pops outer matrix!
glPopMatrix();
#ifdef DEBUG
snprintf(buf, sizeof(buf), "%.2f fps, timer: %d, frames: %d", global.fps.fps, global.timer, global.frames);
#ifdef DEBUGe
snprintf(buf, sizeof(buf), "%.2f lfps, %.2f rfps, timer: %d, frames: %d",
global.fps.logic.fps,
global.fps.render.fps,
global.timer,
global.frames
);
#else
snprintf(buf, sizeof(buf), "%.2f fps", global.fps.fps);
if(get_effective_frameskip() > 1) {
snprintf(buf, sizeof(buf), "%.2f lfps, %.2f rfps",
global.fps.logic.fps,
global.fps.render.fps
);
} else {
snprintf(buf, sizeof(buf), "%.2f fps",
global.fps.logic.fps
);
}
#endif
draw_text(AL_Right | AL_Flag_NoAdjust, SCREEN_W, rint(SCREEN_H - 0.5 * stringheight(buf, _fonts.monosmall)), buf, _fonts.monosmall);
@ -653,7 +667,7 @@ static void stage_draw_framerate_graphs(void) {
glUseProgram(s->prog);
fill_graph(NUM_SAMPLES, samples, &global.fps);
fill_graph(NUM_SAMPLES, samples, &global.fps.logic);
glUniform3f(u_colors[0], 0.0, 1.0, 1.0);
glUniform3f(u_colors[1], 1.0, 1.0, 0.0);
glUniform3f(u_colors[2], 1.0, 0.0, 0.0);
@ -663,7 +677,7 @@ static void stage_draw_framerate_graphs(void) {
// x -= w * 1.1;
y += h + 1;
fill_graph(NUM_SAMPLES, samples, &global.fps_busy);
fill_graph(NUM_SAMPLES, samples, &global.fps.busy);
glUniform3f(u_colors[0], 0.0, 1.0, 0.0);
glUniform3f(u_colors[1], 1.0, 0.0, 0.0);
glUniform3f(u_colors[2], 1.0, 0.0, 0.5);

View file

@ -338,38 +338,67 @@ void fpscounter_update(FPSCounter *fps) {
fps->last_update_time = time_get();
}
void loop_at_fps(FrameAction (*frame_func)(void*), bool (*limiter_cond_func)(void*), void *arg, uint32_t fps) {
assert(frame_func != NULL);
uint32_t get_effective_frameskip(void) {
uint32_t frameskip;
if(global.frameskip > 0) {
frameskip = global.frameskip;
} else {
frameskip = config_get_int(CONFIG_VID_FRAMESKIP);
}
return frameskip;
}
void loop_at_fps(LogicFrameFunc logic_frame, RenderFrameFunc render_frame, void *arg, uint32_t fps) {
assert(logic_frame != NULL);
assert(render_frame != NULL);
assert(fps > 0);
hrtime_t frame_start_time = time_get();
hrtime_t next_frame_time = frame_start_time;
hrtime_t target_frame_time = ((hrtime_t)1.0) / fps;
FrameAction last_frame_action = FRAME_SWAP;
FrameAction rframe_action = RFRAME_SWAP;
FrameAction lframe_action = LFRAME_WAIT;
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 late_swap = config_get_int(CONFIG_VID_LATE_SWAP);
while(last_frame_action != FRAME_STOP) {
uint32_t frame_num = 0;
while(lframe_action != LFRAME_STOP) {
frame_start_time = time_get();
begin_frame:
if(late_swap && last_frame_action == FRAME_SWAP) {
if(late_swap && rframe_action == RFRAME_SWAP) {
video_swap_buffers();
}
global.fps_busy.last_update_time = time_get();
last_frame_action = frame_func(arg);
global.fps.busy.last_update_time = time_get();
if(!late_swap && last_frame_action == FRAME_SWAP) {
++frame_num;
lframe_action = logic_frame(arg);
if(frame_num % get_effective_frameskip()) {
rframe_action = RFRAME_DROP;
} else {
rframe_action = render_frame(arg);
fpscounter_update(&global.fps.render);
}
fpscounter_update(&global.fps.logic);
fpscounter_update(&global.fps.busy);
if(!late_swap && rframe_action == RFRAME_SWAP) {
video_swap_buffers();
}
fpscounter_update(&global.fps);
fpscounter_update(&global.fps_busy);
if(lframe_action == LFRAME_SKIP) {
continue;
}
#ifdef DEBUG
if(gamekeypressed(KEY_FPSLIMIT_OFF)) {
@ -377,38 +406,36 @@ begin_frame:
}
#endif
if(!limiter_cond_func || limiter_cond_func(arg)) {
next_frame_time = frame_start_time + target_frame_time;
next_frame_time = frame_start_time + target_frame_time;
if(compensate) {
hrtime_t rt = time_get();
hrtime_t diff = rt - next_frame_time;
if(compensate) {
hrtime_t rt = time_get();
hrtime_t diff = rt - next_frame_time;
if(diff >= 0) {
// frame took too long...
// try to compensate in the next frame to avoid slowdown
frame_start_time = rt - min(diff, target_frame_time);
goto begin_frame;
if(diff >= 0) {
// frame took too long...
// try to compensate in the next frame to avoid slowdown
frame_start_time = rt - min(diff, target_frame_time);
goto begin_frame;
}
}
if(delay > 0) {
int32_t realdelay = delay;
int32_t maxdelay = (int32_t)(1000 * (next_frame_time - time_get()));
if(realdelay > maxdelay) {
if(exact_delay) {
log_debug("Delay of %i ignored. Minimum is %i but TAISEI_FRAMELIMITER_SLEEP_EXACT is active", realdelay, maxdelay);
realdelay = 0;
} else {
log_debug("Delay reduced from %i to %i", realdelay, maxdelay);
realdelay = maxdelay;
}
}
if(delay > 0) {
int32_t realdelay = delay;
int32_t maxdelay = (int32_t)(1000 * (next_frame_time - time_get()));
if(realdelay > maxdelay) {
if(exact_delay) {
log_debug("Delay of %i ignored. Minimum is %i but TAISEI_FRAMELIMITER_SLEEP_EXACT is active", realdelay, maxdelay);
realdelay = 0;
} else {
log_debug("Delay reduced from %i to %i", realdelay, maxdelay);
realdelay = maxdelay;
}
}
if(realdelay > 0) {
SDL_Delay(realdelay);
}
if(realdelay > 0) {
SDL_Delay(realdelay);
}
}

View file

@ -106,12 +106,19 @@ typedef struct {
} FPSCounter;
typedef enum FrameAction {
FRAME_SWAP,
FRAME_DROP,
FRAME_STOP,
RFRAME_SWAP,
RFRAME_DROP,
LFRAME_WAIT,
LFRAME_SKIP,
LFRAME_STOP,
} FrameAction;
void loop_at_fps(FrameAction (*frame_func)(void*), bool (*limiter_cond_func)(void*), void *arg, uint32_t fps);
typedef FrameAction (*LogicFrameFunc)(void*);
typedef FrameAction (*RenderFrameFunc)(void*);
uint32_t get_effective_frameskip(void);
void loop_at_fps(LogicFrameFunc logic_frame, RenderFrameFunc render_frame, void *arg, uint32_t fps);
void fpscounter_reset(FPSCounter *fps);
void fpscounter_update(FPSCounter *fps);
void set_ortho(void);