Skip to content

Commit 1db9be8

Browse files
committed
Fix off-by-one error in movie checkpoints
The issue was that when recording, the recorded checkpoint incorporates the current frame's inputs. But when playing back, the checkpoint was deserialized *before* the current frame was simulated.
1 parent 37a8155 commit 1db9be8

3 files changed

Lines changed: 70 additions & 44 deletions

File tree

input/bsv/bsvmovie.c

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,25 @@ bool bsv_movie_skip_to_next_checkpoint_impl(bsv_movie_t *movie);
6060
bool bsv_movie_skip_to_prev_checkpoint_impl(bsv_movie_t *movie);
6161
bool bsv_movie_seek_to_pos_impl(bsv_movie_t *movie, int64_t pos);
6262

63+
void bsv_movie_store_frame(bsv_movie_t *handle)
64+
{
65+
retro_ctx_serialize_info_t serial_info;
66+
serial_info.size = core_serialize_size();
67+
if (handle->cur_save_size < serial_info.size && handle->cur_save)
68+
{
69+
free(handle->cur_save);
70+
handle->cur_save = NULL;
71+
}
72+
if (!handle->cur_save)
73+
{
74+
handle->cur_save_size = serial_info.size;
75+
handle->cur_save = malloc(serial_info.size);
76+
handle->cur_save_valid = false;
77+
}
78+
serial_info.data = handle->cur_save;
79+
core_serialize(&serial_info);
80+
}
81+
6382
bool bsv_movie_reset_playback(bsv_movie_t *handle)
6483
{
6584
uint32_t state_size = 0;
@@ -153,6 +172,7 @@ bool bsv_movie_reset_recording(bsv_movie_t *handle)
153172
intfstream_write(handle->file, &compression, 1);
154173
intfstream_write(handle->file, &encoding, 1);
155174
handle->frame_counter = 0;
175+
bsv_movie_store_frame(handle);
156176
state_size = 2 + bsv_movie_write_checkpoint(handle, compression, encoding);
157177
handle->min_file_pos = intfstream_tell(handle->file);
158178
/* Have to write initial state size header too */
@@ -479,6 +499,23 @@ bool bsv_movie_load_checkpoint(bsv_movie_t *handle, uint8_t compression, uint8_t
479499
return ret;
480500
}
481501

502+
void bsv_movie_start_frame(input_driver_state_t *input_st) {
503+
unsigned checkpoint_interval = config_get_ptr()->uints.replay_checkpoint_interval;
504+
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
505+
if (!handle || handle->playback)
506+
return;
507+
/* Is this a checkpoint recording frame? */
508+
if ((input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_FORCE_CHECKPOINT) ||
509+
((checkpoint_interval != 0)
510+
&& (handle->frame_counter > 0)
511+
&& (handle->frame_counter % (checkpoint_interval*60) == 0)))
512+
{
513+
bsv_movie_store_frame(handle);
514+
handle->make_checkpoint_frame = true;
515+
input_st->bsv_movie_state.flags &= ~BSV_FLAG_MOVIE_FORCE_CHECKPOINT;
516+
}
517+
}
518+
482519
int64_t bsv_movie_write_checkpoint(bsv_movie_t *handle, uint8_t compression, uint8_t encoding)
483520
{
484521
int64_t ret = -1;
@@ -487,33 +524,18 @@ int64_t bsv_movie_write_checkpoint(bsv_movie_t *handle, uint8_t compression, uin
487524
size_t size_swap;
488525
uint8_t *encoded_data = NULL, *compressed_encoded_data = NULL;
489526
bool owns_encoded = false, owns_compressed_encoded = false;
490-
retro_ctx_serialize_info_t serial_info;
491-
serial_info.size = core_serialize_size();
492-
if (handle->cur_save_size < serial_info.size)
493-
{
494-
free(handle->cur_save);
495-
handle->cur_save = NULL;
496-
}
497-
if (!handle->cur_save)
498-
{
499-
handle->cur_save_size = serial_info.size;
500-
handle->cur_save = malloc(serial_info.size);
501-
handle->cur_save_valid = false;
502-
}
503-
serial_info.data = handle->cur_save;
504-
core_serialize(&serial_info);
505527
switch (encoding)
506528
{
507529
case REPLAY_CHECKPOINT2_ENCODING_RAW:
508-
encoded_size = serial_info.size;
509-
encoded_data = serial_info.data;
530+
encoded_size = handle->cur_save_size;
531+
encoded_data = handle->cur_save;
510532
break;
511533
#ifdef HAVE_STATESTREAM
512534
case REPLAY_CHECKPOINT2_ENCODING_STATESTREAM:
513-
encoded_size = serial_info.size + serial_info.size / 2;
535+
encoded_size = handle->cur_save_size + handle->cur_save_size / 2;
514536
encoded_data = malloc(encoded_size);
515537
owns_encoded = true;
516-
encoded_size = bsv_movie_write_deduped_state(handle, serial_info.data, serial_info.size, encoded_data, encoded_size);
538+
encoded_size = bsv_movie_write_deduped_state(handle, handle->cur_save, handle->cur_save_size, encoded_data, encoded_size);
517539
break;
518540
#endif
519541
default:
@@ -564,7 +586,7 @@ int64_t bsv_movie_write_checkpoint(bsv_movie_t *handle, uint8_t compression, uin
564586
goto exit;
565587
}
566588
/* uncompressed, unencoded size */
567-
size_ = swap_if_big32(serial_info.size);
589+
size_ = swap_if_big32(handle->cur_save_size);
568590
if (intfstream_write(handle->file, &size_, sizeof(uint32_t)) < (int64_t)sizeof(uint32_t))
569591
{
570592
ret = -1;
@@ -757,6 +779,17 @@ void bsv_movie_scan_from_start(bsv_movie_t *movie, int32_t len)
757779
bsv_movie_scan_to(movie, len);
758780
}
759781

782+
void bsv_movie_dequeue_next(input_driver_state_t *input_st)
783+
{
784+
if (input_st->bsv_movie_state_next_handle)
785+
{
786+
if (input_st->bsv_movie_state_handle)
787+
bsv_movie_deinit(input_st);
788+
input_st->bsv_movie_state_handle = input_st->bsv_movie_state_next_handle;
789+
input_st->bsv_movie_state_next_handle = NULL;
790+
}
791+
}
792+
760793
void bsv_movie_next_frame(input_driver_state_t *input_st)
761794
{
762795
unsigned checkpoint_interval = config_get_ptr()->uints.replay_checkpoint_interval;
@@ -765,14 +798,6 @@ void bsv_movie_next_frame(input_driver_state_t *input_st)
765798
bsv_movie_state_handle to bsv_movie_state_next_handle and clear
766799
next_handle */
767800
bsv_movie_t *handle = input_st->bsv_movie_state_handle;
768-
if (input_st->bsv_movie_state_next_handle)
769-
{
770-
if (handle)
771-
bsv_movie_deinit(input_st);
772-
handle = input_st->bsv_movie_state_next_handle;
773-
input_st->bsv_movie_state_handle = handle;
774-
input_st->bsv_movie_state_next_handle = NULL;
775-
}
776801

777802
if (!handle)
778803
return;
@@ -805,10 +830,7 @@ void bsv_movie_next_frame(input_driver_state_t *input_st)
805830
handle->input_event_count = 0;
806831

807832
/* Maybe record checkpoint */
808-
if ((input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_FORCE_CHECKPOINT) ||
809-
((checkpoint_interval != 0)
810-
&& (handle->frame_counter > 0)
811-
&& (handle->frame_counter % (checkpoint_interval*60) == 0)))
833+
if (handle->make_checkpoint_frame)
812834
{
813835
uint8_t frame_tok = REPLAY_TOKEN_CHECKPOINT2_FRAME;
814836
uint8_t compression = handle->checkpoint_compression;
@@ -817,7 +839,7 @@ void bsv_movie_next_frame(input_driver_state_t *input_st)
817839
#else
818840
uint8_t encoding = REPLAY_CHECKPOINT2_ENCODING_RAW;
819841
#endif
820-
input_st->bsv_movie_state.flags &= ~BSV_FLAG_MOVIE_FORCE_CHECKPOINT;
842+
handle->make_checkpoint_frame = false;
821843
/* "next frame is a checkpoint" */
822844
intfstream_write(handle->file, (uint8_t *)(&frame_tok), sizeof(uint8_t));
823845
/* compression and encoding schemes */
@@ -1276,7 +1298,6 @@ int16_t bsv_movie_read_state(input_driver_state_t *input_st,
12761298
#endif
12771299
return bsv_result;
12781300
}
1279-
input_st->bsv_movie_state.flags |= BSV_FLAG_MOVIE_END;
12801301
return 0;
12811302
}
12821303

input/input_driver.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ struct bsv_movie
277277
#endif
278278

279279
uint8_t checkpoint_compression, checkpoint_encoding;
280+
bool make_checkpoint_frame;
280281

281282
uint8_t *last_save, *cur_save;
282283
size_t last_save_size, cur_save_size;
@@ -1104,6 +1105,7 @@ void input_overlay_check_mouse_cursor(void);
11041105

11051106
#ifdef HAVE_BSV_MOVIE
11061107
void bsv_movie_frame_rewind(void);
1108+
void bsv_movie_start_frame(input_driver_state_t *input_st);
11071109
void bsv_movie_next_frame(input_driver_state_t *input_st);
11081110
bool bsv_movie_read_next_events(bsv_movie_t *handle, replay_checkpoint_behavior checkpoint_behavior, bool end_movie_on_eof);
11091111
bool bsv_movie_reset_playback(bsv_movie_t *handle);
@@ -1112,6 +1114,7 @@ void bsv_movie_finish_rewind(input_driver_state_t *input_st);
11121114
void bsv_movie_deinit(input_driver_state_t *input_st);
11131115
void bsv_movie_deinit_full(input_driver_state_t *input_st);
11141116
void bsv_movie_enqueue(input_driver_state_t *input_st, bsv_movie_t *state, enum bsv_flags flags);
1117+
void bsv_movie_dequeue_next(input_driver_state_t *input_st);
11151118

11161119
bool movie_commit_checkpoint(input_driver_state_t *input_st);
11171120
bool movie_skip_to_prev_checkpoint(input_driver_state_t *input_st);

runloop.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7201,6 +7201,9 @@ int runloop_iterate(void)
72017201
#endif
72027202
}
72037203
#endif
7204+
#ifdef HAVE_BSV_MOVIE
7205+
bsv_movie_dequeue_next(input_st);
7206+
#endif
72047207

72057208
if (runloop_st->frame_time.callback)
72067209
{
@@ -7354,10 +7357,6 @@ int runloop_iterate(void)
73547357
autosave_lock();
73557358
#endif
73567359

7357-
#ifdef HAVE_BSV_MOVIE
7358-
bsv_movie_next_frame(input_st);
7359-
#endif
7360-
73617360
if ( settings->bools.camera_allow
73627361
&& camera_st->cb.caps
73637362
&& camera_st->driver
@@ -7370,6 +7369,10 @@ int runloop_iterate(void)
73707369
/* Measure the time between core_run() and video_driver_frame() */
73717370
runloop_st->core_run_time = cpu_features_get_time_usec();
73727371

7372+
#ifdef HAVE_BSV_MOVIE
7373+
bsv_movie_start_frame(input_st);
7374+
#endif
7375+
73737376
{
73747377
#ifdef HAVE_RUNAHEAD
73757378
bool run_ahead_enabled = settings->bools.run_ahead_enabled;
@@ -7396,6 +7399,11 @@ int runloop_iterate(void)
73967399
#endif
73977400
core_run();
73987401
}
7402+
#ifdef HAVE_BSV_MOVIE
7403+
bsv_movie_finish_rewind(input_st);
7404+
bsv_movie_next_frame(input_st);
7405+
#endif
7406+
73997407

74007408
/* Increment runtime tick counter after each call to
74017409
* core_run() or run_ahead() */
@@ -7413,12 +7421,6 @@ int runloop_iterate(void)
74137421
presence_update(PRESENCE_GAME);
74147422
#endif
74157423
#ifdef HAVE_BSV_MOVIE
7416-
bsv_movie_finish_rewind(input_st);
7417-
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
7418-
{
7419-
movie_stop_playback(input_st);
7420-
command_event(CMD_EVENT_PAUSE, NULL);
7421-
}
74227424
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_END)
74237425
{
74247426
movie_stop_playback(input_st);

0 commit comments

Comments
 (0)