Simple rewind buffer
Jeffrey Pfau jeffrey@endrift.com
Sun, 26 Jan 2014 02:50:28 -0800
6 files changed,
72 insertions(+),
1 deletions(-)
M
src/gba/gba-serialize.c
→
src/gba/gba-serialize.c
@@ -2,6 +2,7 @@ #include "gba-serialize.h"
#include "gba-audio.h" #include "gba-io.h" +#include "gba-thread.h" #include "memory.h" #include <fcntl.h>@@ -101,10 +102,41 @@ struct GBASerializedState* GBAMapState(int fd) {
return fileMemoryMap(fd, sizeof(struct GBASerializedState), MEMORY_WRITE); } -struct GBASerializedState* GBAAloocateState(void) { +struct GBASerializedState* GBAAllocateState(void) { return anonymousMemoryMap(sizeof(struct GBASerializedState)); } void GBADeallocateState(struct GBASerializedState* state) { mappedMemoryFree(state, sizeof(struct GBASerializedState)); } + +void GBARecordFrame(struct GBAThread* thread) { + int offset = thread->rewindBufferWriteOffset; + struct GBASerializedState* state = thread->rewindBuffer[offset]; + if (!state) { + state = GBAAllocateState(); + thread->rewindBuffer[offset] = state; + } + GBASerialize(thread->gba, state); + thread->rewindBufferSize = thread->rewindBufferSize == thread->rewindBufferCapacity ? thread->rewindBufferCapacity : thread->rewindBufferSize + 1; + thread->rewindBufferWriteOffset = (offset + 1) % thread->rewindBufferSize; +} + +void GBARewind(struct GBAThread* thread, int nStates) { + if (nStates > thread->rewindBufferSize || nStates < 0) { + nStates = thread->rewindBufferSize; + } + if (nStates == 0) { + return; + } + int offset = thread->rewindBufferWriteOffset - nStates; + if (offset < 0) { + offset += thread->rewindBufferSize; + } + struct GBASerializedState* state = thread->rewindBuffer[offset]; + if (!state) { + return; + } + thread->rewindBufferSize -= nStates; + GBADeserialize(thread->gba, state); +}
M
src/gba/gba-serialize.h
→
src/gba/gba-serialize.h
@@ -233,4 +233,8 @@ struct GBASerializedState* GBAMapState(int fd);
struct GBASerializedState* GBAAllocateState(void); void GBADeallocateState(struct GBASerializedState* state); +struct GBAThread; +void GBARecordFrame(struct GBAThread* thread); +void GBARewind(struct GBAThread* thread, int nStates); + #endif
M
src/gba/gba-thread.c
→
src/gba/gba-thread.c
@@ -3,6 +3,7 @@
#include "arm.h" #include "debugger.h" #include "gba.h" +#include "gba-serialize.h" #include <stdlib.h> #include <signal.h>@@ -157,6 +158,12 @@ threadContext->state = THREAD_INITIALIZED;
threadContext->sync.videoFrameOn = 1; threadContext->sync.videoFrameSkip = 0; + threadContext->rewindBufferNext = threadContext->rewindBufferInterval; + threadContext->rewindBufferSize = 0; + if (threadContext->rewindBufferCapacity) { + threadContext->rewindBuffer = calloc(threadContext->rewindBufferCapacity, sizeof(void*)); + } + MutexInit(&threadContext->stateMutex); ConditionInit(&threadContext->stateCond);@@ -204,6 +211,14 @@
ConditionWake(&threadContext->sync.audioRequiredCond); ConditionDeinit(&threadContext->sync.audioRequiredCond); MutexDeinit(&threadContext->sync.audioBufferMutex); + + int i; + for (i = 0; i < threadContext->rewindBufferCapacity; ++i) { + if (threadContext->rewindBuffer[i]) { + GBADeallocateState(threadContext->rewindBuffer[i]); + } + } + free(threadContext->rewindBuffer); } void GBAThreadPause(struct GBAThread* threadContext) {@@ -293,6 +308,13 @@ }
MutexUnlock(&sync->videoFrameMutex); struct GBAThread* thread = GBAThreadGetContext(); + if (thread->rewindBuffer) { + --thread->rewindBufferNext; + if (thread->rewindBufferNext <= 0) { + thread->rewindBufferNext = thread->rewindBufferInterval; + GBARecordFrame(thread); + } + } if (thread->frameCallback) { thread->frameCallback(thread); }
M
src/gba/gba-thread.h
→
src/gba/gba-thread.h
@@ -52,6 +52,13 @@ int audioWait;
Condition audioRequiredCond; Mutex audioBufferMutex; } sync; + + int rewindBufferSize; + int rewindBufferCapacity; + int rewindBufferInterval; + int rewindBufferNext; + struct GBASerializedState** rewindBuffer; + int rewindBufferWriteOffset; }; int GBAThreadStart(struct GBAThread* threadContext);
M
src/platform/sdl/gl-main.c
→
src/platform/sdl/gl-main.c
@@ -80,6 +80,8 @@ context.startCallback = _GBASDLStart;
context.cleanCallback = _GBASDLClean; context.frameCallback = 0; context.userData = &renderer; + context.rewindBufferCapacity = 10; + context.rewindBufferInterval = 30; GBAThreadStart(&context); _GBASDLRunloop(&context, &renderer);
M
src/platform/sdl/sdl-events.c
→
src/platform/sdl/sdl-events.c
@@ -68,6 +68,10 @@ #endif
case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return; + case SDLK_LEFTBRACKET: + GBAThreadPause(context); + GBARewind(context, 10); + GBAThreadUnpause(context); default: if (event->type == SDL_KEYDOWN) { if (event->keysym.mod & KMOD_CTRL) {