mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-23 16:09:47 +00:00
Cleanup and optimize rewind a bit. Forced POT buffer size.
This commit is contained in:
parent
f3e487557e
commit
0c3f6ed66b
@ -142,7 +142,7 @@ struct settings
|
|||||||
char screenshot_directory[MAXPATHLEN];
|
char screenshot_directory[MAXPATHLEN];
|
||||||
|
|
||||||
bool rewind_enable;
|
bool rewind_enable;
|
||||||
unsigned rewind_buffer_size;
|
size_t rewind_buffer_size;
|
||||||
unsigned rewind_granularity;
|
unsigned rewind_granularity;
|
||||||
|
|
||||||
bool pause_nonactive;
|
bool pause_nonactive;
|
||||||
|
64
rewind.c
64
rewind.c
@ -21,11 +21,14 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include "general.h"
|
||||||
|
|
||||||
struct state_manager
|
struct state_manager
|
||||||
{
|
{
|
||||||
uint64_t *buffer;
|
uint64_t *buffer;
|
||||||
size_t buf_size;
|
size_t buf_size;
|
||||||
|
size_t buf_size_mask;
|
||||||
uint32_t *tmp_state;
|
uint32_t *tmp_state;
|
||||||
size_t top_ptr;
|
size_t top_ptr;
|
||||||
size_t bottom_ptr;
|
size_t bottom_ptr;
|
||||||
@ -33,19 +36,48 @@ struct state_manager
|
|||||||
bool first_pop;
|
bool first_pop;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline size_t nearest_pow2_size(size_t v)
|
||||||
|
{
|
||||||
|
size_t orig = v;
|
||||||
|
v--;
|
||||||
|
v |= v >> 1;
|
||||||
|
v |= v >> 2;
|
||||||
|
v |= v >> 4;
|
||||||
|
if (sizeof(v) * CHAR_BIT >= 16)
|
||||||
|
v |= v >> 8;
|
||||||
|
if (sizeof(v) * CHAR_BIT >= 32)
|
||||||
|
v |= v >> 16;
|
||||||
|
if (sizeof(v) * CHAR_BIT >= 64)
|
||||||
|
v |= v >> 32;
|
||||||
|
v++;
|
||||||
|
|
||||||
|
size_t next = v;
|
||||||
|
size_t prev = v >> 1;
|
||||||
|
|
||||||
|
if ((next - orig) < (orig - prev))
|
||||||
|
return next;
|
||||||
|
else
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer)
|
state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer)
|
||||||
{
|
{
|
||||||
if (buffer_size <= state_size * 2 + 1) // Need a sufficient buffer size.
|
if (buffer_size <= state_size * 4) // Need a sufficient buffer size.
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
state_manager_t *state = calloc(1, sizeof(*state));
|
state_manager_t *state = calloc(1, sizeof(*state));
|
||||||
if (!state)
|
if (!state)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
assert(state_size % 4 == 0); // We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
// We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
||||||
|
assert(state_size % 4 == 0);
|
||||||
state->top_ptr = 1;
|
state->top_ptr = 1;
|
||||||
state->state_size = (state_size + 3) >> 2; // Multiple of 4.
|
|
||||||
state->buf_size = (buffer_size + 7) >> 3; // Multiple of 8.
|
state->state_size = state_size / sizeof(uint32_t); // Works in multiple of 4.
|
||||||
|
state->buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8.
|
||||||
|
state->buf_size_mask = state->buf_size - 1;
|
||||||
|
SSNES_LOG("Readjusted rewind buffer size to %u MiB\n", (unsigned)(sizeof(uint64_t) * (state->buf_size >> 20)));
|
||||||
|
|
||||||
if (!(state->buffer = calloc(1, state->buf_size * sizeof(uint64_t))))
|
if (!(state->buffer = calloc(1, state->buf_size * sizeof(uint64_t))))
|
||||||
goto error;
|
goto error;
|
||||||
if (!(state->tmp_state = calloc(1, state->state_size * sizeof(uint32_t))))
|
if (!(state->tmp_state = calloc(1, state->state_size * sizeof(uint32_t))))
|
||||||
@ -81,14 +113,11 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->top_ptr == 0)
|
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||||
state->top_ptr = state->buf_size - 1;
|
|
||||||
else
|
|
||||||
state->top_ptr--;
|
|
||||||
|
|
||||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||||
{
|
{
|
||||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,15 +128,12 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
|||||||
uint32_t xor = state->buffer[state->top_ptr] & 0xFFFFFFFFU;
|
uint32_t xor = state->buffer[state->top_ptr] & 0xFFFFFFFFU;
|
||||||
state->tmp_state[addr] ^= xor;
|
state->tmp_state[addr] ^= xor;
|
||||||
|
|
||||||
if (state->top_ptr == 0)
|
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||||
state->top_ptr = state->buf_size - 1;
|
|
||||||
else
|
|
||||||
state->top_ptr--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||||
{
|
{
|
||||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,9 +142,9 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
|||||||
|
|
||||||
static void reassign_bottom(state_manager_t *state)
|
static void reassign_bottom(state_manager_t *state)
|
||||||
{
|
{
|
||||||
state->bottom_ptr = (state->top_ptr + 1) % state->buf_size;
|
state->bottom_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||||
while (state->buffer[state->bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta).
|
while (state->buffer[state->bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta).
|
||||||
state->bottom_ptr = (state->bottom_ptr + 1) % state->buf_size;
|
state->bottom_ptr = (state->bottom_ptr + 1) & state->buf_size_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void generate_delta(state_manager_t *state, const void *data)
|
static void generate_delta(state_manager_t *state, const void *data)
|
||||||
@ -128,7 +154,9 @@ static void generate_delta(state_manager_t *state, const void *data)
|
|||||||
const uint32_t *new_state = data;
|
const uint32_t *new_state = data;
|
||||||
|
|
||||||
state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
|
state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
|
||||||
if (state->top_ptr == state->bottom_ptr) // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
|
||||||
|
// Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||||
|
if (state->top_ptr == state->bottom_ptr)
|
||||||
crossed = true;
|
crossed = true;
|
||||||
|
|
||||||
for (uint64_t i = 0; i < state->state_size; i++)
|
for (uint64_t i = 0; i < state->state_size; i++)
|
||||||
@ -142,7 +170,7 @@ static void generate_delta(state_manager_t *state, const void *data)
|
|||||||
if (xor)
|
if (xor)
|
||||||
{
|
{
|
||||||
state->buffer[state->top_ptr] = (i << 32) | xor;
|
state->buffer[state->top_ptr] = (i << 32) | xor;
|
||||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||||
|
|
||||||
if (state->top_ptr == state->bottom_ptr)
|
if (state->top_ptr == state->bottom_ptr)
|
||||||
crossed = true;
|
crossed = true;
|
||||||
|
@ -400,7 +400,7 @@ static void parse_config_file(void)
|
|||||||
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
|
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
|
||||||
|
|
||||||
if (config_get_int(conf, "rewind_buffer_size", &tmp_int))
|
if (config_get_int(conf, "rewind_buffer_size", &tmp_int))
|
||||||
g_settings.rewind_buffer_size = tmp_int * 1000000;
|
g_settings.rewind_buffer_size = tmp_int * 1000000LLU;
|
||||||
|
|
||||||
CONFIG_GET_INT(rewind_granularity, "rewind_granularity");
|
CONFIG_GET_INT(rewind_granularity, "rewind_granularity");
|
||||||
|
|
||||||
|
2
ssnes.c
2
ssnes.c
@ -1039,7 +1039,7 @@ static void init_rewind(void)
|
|||||||
size_t serial_size = psnes_serialize_size();
|
size_t serial_size = psnes_serialize_size();
|
||||||
g_extern.state_buf = calloc(1, (serial_size + 3) & ~3); // Make sure we allocate at least 4-byte multiple.
|
g_extern.state_buf = calloc(1, (serial_size + 3) & ~3); // Make sure we allocate at least 4-byte multiple.
|
||||||
psnes_serialize(g_extern.state_buf, serial_size);
|
psnes_serialize(g_extern.state_buf, serial_size);
|
||||||
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)g_settings.rewind_buffer_size / 1000000);
|
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)(g_settings.rewind_buffer_size / 1000000));
|
||||||
g_extern.state_manager = state_manager_new((serial_size + 3) & ~3, g_settings.rewind_buffer_size, g_extern.state_buf);
|
g_extern.state_manager = state_manager_new((serial_size + 3) & ~3, g_settings.rewind_buffer_size, g_extern.state_buf);
|
||||||
if (!g_extern.state_manager)
|
if (!g_extern.state_manager)
|
||||||
SSNES_WARN("Failed to init rewind buffer. Rewinding will be disabled!\n");
|
SSNES_WARN("Failed to init rewind buffer. Rewinding will be disabled!\n");
|
||||||
|
Loading…
Reference in New Issue
Block a user